wip: continue implementation
This commit is contained in:
@@ -8,3 +8,4 @@ htmx.config.historyEnabled = false
|
||||
htmx.config.historyCacheSize = 0
|
||||
htmx.config.allowScriptTags = false
|
||||
htmx.config.withCredentials = true
|
||||
htmx.config.selfRequestsOnly = false
|
||||
@@ -1,5 +0,0 @@
|
||||
import { Controller} from '@hotwired/stimulus'
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ['field']
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Controller} from '@hotwired/stimulus'
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ['field']
|
||||
static values = { availabilities: Object }
|
||||
|
||||
toggle(event) {
|
||||
let field = event.target
|
||||
let id = field.value
|
||||
let availabilities = this.availabilitiesValue
|
||||
if (field.checked) {
|
||||
availabilities[id].available--
|
||||
} else {
|
||||
availabilities[id].available++
|
||||
}
|
||||
field.disabled = 0 >= availabilities[id].available
|
||||
field.classList.toggle('cursor-not-allowed', field.disabled)
|
||||
this.availabilitiesValue = availabilities
|
||||
}
|
||||
|
||||
availabilitiesValueChanged(availabilities) {
|
||||
this.fieldTargets.forEach((field) => {
|
||||
let id = field.value
|
||||
if (false === field.checked && 0 >= availabilities[id].available) {
|
||||
field.disabled = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import Toastify from 'toastify-js'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static values = { text: String, class: String }
|
||||
|
||||
connect() {
|
||||
Toastify({
|
||||
duration: 5000,
|
||||
text: this.textValue,
|
||||
gravity: 'top',
|
||||
position: 'right',
|
||||
className: this.classValue,
|
||||
close: true,
|
||||
}).showToast()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import tippy from 'tippy.js'
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ['trigger', 'template']
|
||||
static values = { content: String, placement: String }
|
||||
|
||||
connect() {
|
||||
let options, element
|
||||
let placement = this.placementValue ? this.placementValue : 'top'
|
||||
if (this.hasTemplateTarget) {
|
||||
options = {
|
||||
content: this.templateTarget.innerHTML,
|
||||
placement,
|
||||
touch: true,
|
||||
trigger: 'click',
|
||||
allowHTML: true,
|
||||
interactive: true,
|
||||
}
|
||||
} else {
|
||||
options = {
|
||||
content: this.contentValue,
|
||||
placement,
|
||||
touch: false,
|
||||
allowHTML: true,
|
||||
interactive: true,
|
||||
}
|
||||
}
|
||||
if (this.hasTriggerTarget) {
|
||||
element = this.triggerTarget
|
||||
} else {
|
||||
element = this.element
|
||||
}
|
||||
tippy(element, options)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
@import "components/forms.css";
|
||||
@import "components/button.css";
|
||||
@import "components/menu.css";
|
||||
/*@import "components/toast.css";*/
|
||||
/*@import "components/tooltip.css";*/
|
||||
@import "components/toast.css";
|
||||
@import "components/tooltip.css";
|
||||
Generated
+26
@@ -6,6 +6,10 @@
|
||||
"": {
|
||||
"name": "myep-next",
|
||||
"license": "WTFPL",
|
||||
"dependencies": {
|
||||
"tippy.js": "^6.3.7",
|
||||
"toastify-js": "^1.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.0",
|
||||
"@babel/preset-env": "^7.16.0",
|
||||
@@ -1807,6 +1811,15 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.27.8",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||
@@ -6485,6 +6498,14 @@
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/tippy.js": {
|
||||
"version": "6.3.7",
|
||||
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
|
||||
@@ -6506,6 +6527,11 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toastify-js": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz",
|
||||
"integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ=="
|
||||
},
|
||||
"node_modules/ts-interface-checker": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||
|
||||
@@ -27,5 +27,9 @@
|
||||
"dev": "encore dev",
|
||||
"watch": "encore dev --watch",
|
||||
"build": "encore production --progress"
|
||||
},
|
||||
"dependencies": {
|
||||
"tippy.js": "^6.3.7",
|
||||
"toastify-js": "^1.12.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\BusProNet\ApiResponseParser;
|
||||
|
||||
use App\BusProNet\Model\CrmAction;
|
||||
use App\BusProNet\Model\CrmAttributes;
|
||||
use App\BusProNet\Model\CrmSelection;
|
||||
use App\BusProNet\Model\CrmSelectionGroup;
|
||||
@@ -18,11 +19,13 @@ class CrmAttributesResponseParser
|
||||
public function parse(\SimpleXMLElement $xml): CrmAttributes
|
||||
{
|
||||
$groups = [];
|
||||
$actions = [];
|
||||
$isAdmin = $isManager = $isHouseManager = $isTeamer = false;
|
||||
$hotelCode = null;
|
||||
|
||||
foreach ($xml->selektionsmerkmale->selektionsgruppe as $item) {
|
||||
$group = new CrmSelectionGroup();
|
||||
$group->id = (int) $item->attributes()['id'];
|
||||
$group->label = (string) $item->attributes()['bezeichnung'];
|
||||
$attributes = [];
|
||||
|
||||
@@ -32,6 +35,7 @@ class CrmAttributesResponseParser
|
||||
$attribute = new CrmSelection();
|
||||
$attribute->id = (int) $subItemAttributes['id'];
|
||||
$attribute->label = $attributeLabel;
|
||||
$attribute->mutable = $this->stringToBool((string) $subItemAttributes['aenderbar']);
|
||||
$attribute->selected = $this->stringToBool((string) $subItemAttributes['auswahl']);
|
||||
|
||||
if (1 === preg_match('/^Hausleitung ([A-Z0-9]+)$/', $attributeLabel, $matches) && true === $attribute->selected) {
|
||||
@@ -54,8 +58,21 @@ class CrmAttributesResponseParser
|
||||
$groups[] = $group;
|
||||
}
|
||||
|
||||
foreach ($xml->crmaktionen->crmaktion as $item) {
|
||||
$attributes = $item->attributes();
|
||||
$crmAction = new CrmAction();
|
||||
$crmAction->id = (int) $attributes['id'];
|
||||
$crmAction->code = (string) $attributes['code'];
|
||||
$crmAction->label = (string) $attributes['bezeichnung'];
|
||||
$crmAction->mutable = $this->stringToBool((string) $attributes['aenderbar']);
|
||||
$crmAction->selected = $this->stringToBool((string) $attributes['auswahl']);
|
||||
|
||||
$actions[] = $crmAction;
|
||||
}
|
||||
|
||||
$response = new CrmAttributes();
|
||||
$response->selectionGroups = $groups;
|
||||
$response->crmActions = $actions;
|
||||
$response->admin = $isAdmin;
|
||||
$response->manager = $isManager;
|
||||
$response->houseManager = $isHouseManager;
|
||||
|
||||
@@ -2,10 +2,18 @@
|
||||
|
||||
namespace App\BusProNet\Model;
|
||||
|
||||
class Availability
|
||||
class Availability implements \JsonSerializable
|
||||
{
|
||||
public ?int $serviceId = null;
|
||||
public ?string $status = null;
|
||||
public ?int $available = null;
|
||||
public ?float $price = null;
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->serviceId,
|
||||
'available' => $this->available,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\BusProNet\Model;
|
||||
|
||||
class CrmAction
|
||||
{
|
||||
public ?int $id = null;
|
||||
public ?string $code = null;
|
||||
public ?string $label = null;
|
||||
public bool $mutable = false;
|
||||
public bool $selected = false;
|
||||
}
|
||||
@@ -5,30 +5,10 @@ namespace App\BusProNet\Model;
|
||||
class CrmAttributes
|
||||
{
|
||||
public ?array $selectionGroups = null;
|
||||
public ?array $crmActions = null;
|
||||
public bool $admin = false;
|
||||
public bool $manager = false;
|
||||
public bool $houseManager = false;
|
||||
public bool $teamer = false;
|
||||
public ?string $hotelCode = null;
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
$crmSelections = [];
|
||||
|
||||
foreach ($this->selectionGroups as $group) {
|
||||
/** @var CrmSelectionGroup $group */
|
||||
if (false === isset($crmSelections[$group->label])) {
|
||||
$crmSelections[$group->label] = [];
|
||||
}
|
||||
foreach ($group->selections as $attribute) {
|
||||
/** @var CrmSelection $attribute */
|
||||
if (false === $attribute->selected) {
|
||||
continue;
|
||||
}
|
||||
$crmSelections[$group->label][] = $attribute->label;
|
||||
}
|
||||
}
|
||||
|
||||
return $crmSelections;
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,6 @@ class CrmSelection
|
||||
{
|
||||
public ?int $id = null;
|
||||
public ?string $label = null;
|
||||
public bool $mutable = false;
|
||||
public bool $selected = false;
|
||||
}
|
||||
@@ -19,4 +19,16 @@ class CrmSelectionGroup
|
||||
public ?int $id = null;
|
||||
public ?string $label = null;
|
||||
public ?array $selections = null;
|
||||
|
||||
public function getMutableSelections(): array
|
||||
{
|
||||
return array_filter($this->selections, function (CrmSelection $selection) {
|
||||
return $selection->mutable;
|
||||
});
|
||||
}
|
||||
|
||||
public function isVisible(): bool
|
||||
{
|
||||
return 0 < count($this->getMutableSelections());
|
||||
}
|
||||
}
|
||||
@@ -32,14 +32,14 @@ class PersonalDataController extends AbstractController
|
||||
->getPersonalData($bpnUser->getEmail(), $bpnUser->getPassword())
|
||||
;
|
||||
|
||||
$form = $this->createForm(PersonalDataType::class, $personalData, [
|
||||
$personalDataForm = $this->createForm(PersonalDataType::class, $personalData, [
|
||||
'attr' => ['novalidate' => 'novalidate'],
|
||||
'hx_post' => $this->generateUrl('app_personal_data'),
|
||||
'hx_target' => '#app',
|
||||
]);
|
||||
$form->handleRequest($request);
|
||||
$personalDataForm->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
if ($personalDataForm->isSubmitted() && $personalDataForm->isValid()) {
|
||||
try {
|
||||
$this->apiClient->updatePersonalData($bpnUser->getEmail(), $bpnUser->getPassword(), $personalData);
|
||||
$this->addFlash('success', 'Deine persönlichen Daten wurden aktualisiert');
|
||||
@@ -52,7 +52,7 @@ class PersonalDataController extends AbstractController
|
||||
|
||||
return $this->render('personal_data/index.html.twig', [
|
||||
'personalData' => $personalData,
|
||||
'form' => $form->createView(),
|
||||
'personalDataForm' => $personalDataForm->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ class ParticipantData
|
||||
public ?string $gender = null;
|
||||
public ?string $nationality = null;
|
||||
|
||||
#[Assert\Range(notInRangeMessage: 'Bitte einen Wert im Bereich {{ min }}-{{ max }}cm angeben', min: 140, max: 210)]
|
||||
#[Assert\Range(notInRangeMessage: 'Bitte einen Wert im Bereich {{ min }}-{{ max }}cm angeben', min: 150, max: 210)]
|
||||
public ?string $height = null;
|
||||
|
||||
#[Assert\Range(notInRangeMessage: 'Bitte einen Wert im Bereich {{ min }}-{{ max }} angeben', min: 35, max: 49)]
|
||||
#[Assert\Range(notInRangeMessage: 'Bitte einen Wert im Bereich {{ min }}-{{ max }} angeben', min: 37, max: 49)]
|
||||
public ?string $shoeSize = null;
|
||||
|
||||
#[Assert\Range(notInRangeMessage: 'Bitte einen Wert im Bereich {{ min }}-{{ max }}kg angeben', min: 40, max: 130)]
|
||||
|
||||
@@ -21,7 +21,6 @@ class ParticipantType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
if (true === $options['personal_data_mutable']) {
|
||||
$builder
|
||||
->add('firstName', TextType::class, [
|
||||
'label' => 'Vorname',
|
||||
@@ -69,7 +68,6 @@ class ParticipantType extends AbstractType
|
||||
'required' => false,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) {
|
||||
/** @var ParticipantData $participantData */
|
||||
@@ -99,8 +97,8 @@ class ParticipantType extends AbstractType
|
||||
},
|
||||
'choice_attr' => function (?Service $service) {
|
||||
$attributes = [
|
||||
'data-available' => $service->available,
|
||||
'data-availabilities-target' => 'field',
|
||||
'data-booking-target' => 'field',
|
||||
'data-action' => 'booking#toggle',
|
||||
];
|
||||
|
||||
// forcibly select mandatory services
|
||||
@@ -131,7 +129,6 @@ class ParticipantType extends AbstractType
|
||||
];
|
||||
|
||||
// Additional services
|
||||
if (true === $options['additional_services_mutable']) {
|
||||
if (0 < count($options['selectable_courses'])) {
|
||||
$form->add('courses', ChoiceType::class, [
|
||||
...$commonChoiceFieldOptions,
|
||||
@@ -167,21 +164,19 @@ class ParticipantType extends AbstractType
|
||||
'choices' => $options['selectable_rentals'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Transportation
|
||||
if (true === $options['transportation_services_mutable']) {
|
||||
$form
|
||||
->add('transportationServiceTo', ChoiceType::class, [
|
||||
...$commonChoiceFieldOptions,
|
||||
'label' => 'Anreise',
|
||||
'required' => true,
|
||||
'multiple' => false,
|
||||
'choices' => $options['selectable_transportation_services_to'],
|
||||
'choice_attr' => function (?Service $service) {
|
||||
return [
|
||||
'data-available' => $service->available,
|
||||
'data-availabilities-target' => 'field',
|
||||
'data-action' => 'select-toggle#toggle',
|
||||
'data-booking-target' => 'field',
|
||||
'data-action' => 'select-toggle#toggle booking#toggle',
|
||||
'data-select-toggle-value' => $service->subType,
|
||||
];
|
||||
},
|
||||
@@ -189,13 +184,13 @@ class ParticipantType extends AbstractType
|
||||
->add('transportationServiceFro', ChoiceType::class, [
|
||||
...$commonChoiceFieldOptions,
|
||||
'label' => 'Rückreise',
|
||||
'required' => true,
|
||||
'multiple' => false,
|
||||
'choices' => $options['selectable_transportation_services_fro'],
|
||||
]);
|
||||
}
|
||||
])
|
||||
;
|
||||
|
||||
// Pickup
|
||||
if (true === $options['pickups_mutable']) {
|
||||
$form
|
||||
->add('pickup', ChoiceType::class, [
|
||||
'label' => 'Zustieg',
|
||||
@@ -219,8 +214,8 @@ class ParticipantType extends AbstractType
|
||||
number_format($price, 2, ',', '.')
|
||||
);
|
||||
},
|
||||
]);
|
||||
}
|
||||
])
|
||||
;
|
||||
})
|
||||
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
|
||||
$data = $event->getData();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{% if app.session.flashBag.peek('error')|length > 0 %}
|
||||
{% include '_partials/_alert.html.twig' with { level: 'warning', messages: app.flashes('error') } %}
|
||||
{% include '_partials/_toast.html.twig' with { level: 'warning', messages: app.flashes('error') } %}
|
||||
{% endif %}
|
||||
{% if app.session.flashBag.peek('success')|length > 0 %}
|
||||
{% include '_partials/_alert.html.twig' with { level: 'success', messages: app.flashes('success') } %}
|
||||
{% include '_partials/_toast.html.twig' with { level: 'success', messages: app.flashes('success') } %}
|
||||
{% endif %}
|
||||
{% if app.session.flashBag.peek('info')|length > 0 %}
|
||||
{% include '_partials/_alert.html.twig' with { level: 'info', messages: app.flashes('info') } %}
|
||||
{% include '_partials/_toast.html.twig' with { level: 'info', messages: app.flashes('info') } %}
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{% for message in messages %}
|
||||
{% if message is iterable %}
|
||||
{% set message = message.id | trans(message.parameters | default({}), message.domain | default(null), message.locale | default(null)) | raw %}
|
||||
{% else %}
|
||||
{% set message = message | trans | raw %}
|
||||
{% endif %}
|
||||
<div {{ stimulus_controller('toast', { 'text': message, 'class': 'toastify--' ~ level }) }}></div>
|
||||
{% endfor %}
|
||||
@@ -12,6 +12,5 @@
|
||||
</head>
|
||||
<body class="bg-white text-gray-700 font-sans antialiased text-sm">
|
||||
{% block body %}{% endblock %}
|
||||
{% include '_partials/_flashes.html.twig' %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,34 +3,57 @@
|
||||
{% block content %}
|
||||
{{ form_start(form) }}
|
||||
{% if not form.vars.valid %}
|
||||
<article>
|
||||
<header>
|
||||
<strong>Fehler</strong>
|
||||
</header>
|
||||
{{ form_errors(form) }}
|
||||
</article>
|
||||
<div class="rounded-md bg-red-50 p-4 mb-4">
|
||||
<div class="flex">
|
||||
<div class="shrink-0">
|
||||
<svg class="size-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
{% if form.vars.errors|length > 0 %}
|
||||
<h3 class="text-sm font-medium text-red-800">
|
||||
Die Buchung konnte nicht aktualisiert werden
|
||||
</h3>
|
||||
<div class="mt-2 text-sm text-red-700">
|
||||
<ul role="list" class="list-disc space-y-1 pl-5">
|
||||
{% for error in form.vars.errors %}
|
||||
<li>
|
||||
{{ error.message }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<h3 class="text-sm font-medium text-red-800">
|
||||
Die Buchung konnte nicht aktualisiert werden. Problematische Eingaben wurden entsprechend markiert.
|
||||
</h3>
|
||||
{% endif %}
|
||||
<div class="flex flex-col space-y-8 pb-4">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="flex flex-col space-y-8 pb-4" {{ stimulus_controller('booking', { 'availabilities': availabilities.items }) }}>
|
||||
<div class="p-8 border border-gray-300 rounded-md"
|
||||
{{ stimulus_controller('availabilities')|stimulus_controller('toggle', { 'open': true }, { 'closed': 'hidden' }) }}>
|
||||
{{ stimulus_controller('toggle', { 'open': true }, { 'closed': 'hidden' }) }}>
|
||||
<div role="button" class="flex items-center justify-between" {{ stimulus_action('toggle', 'toggle', 'click') }}>
|
||||
<span class="block text-3xl font-semibold">Reisedaten</span>
|
||||
<span class="block text-2xl font-semibold">Reisedaten</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" {{ stimulus_target('toggle', 'icon') }}>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-4 pt-4" {{ stimulus_target('toggle', 'toggle') }}>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-x-8 gap-y-4 pt-4" {{ stimulus_target('toggle', 'toggle') }}>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">
|
||||
Destination
|
||||
Reise
|
||||
</h3>
|
||||
{{ bookingData.travel }}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">
|
||||
Reisedatum
|
||||
Reisezeitraum
|
||||
</h3>
|
||||
{{ bookingData.travelDate|date('d.m.Y') }}
|
||||
{{ travelData.dateFrom|date('d.m.Y') }} - {{ travelData.dateTo|date('d.m.Y') }}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">
|
||||
@@ -38,6 +61,24 @@
|
||||
</h3>
|
||||
{{ bookingData.bookingDate|date('d.m.Y') }}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">
|
||||
Anmelder
|
||||
</h3>
|
||||
{{ bookingData.applicant.firstName }} {{ bookingData.applicant.name }}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">
|
||||
Anzahl Teilnehmer
|
||||
</h3>
|
||||
{{ bookingData.participants|length }}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">
|
||||
Gesamtpreis
|
||||
</h3>
|
||||
{{ bookingData.totalPrice|format_currency('EUR') }}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">
|
||||
Inklusivleistungen
|
||||
@@ -52,39 +93,65 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">
|
||||
Aktualisierung möglich bis
|
||||
</h3>
|
||||
<ul class="list-disc pl-4">
|
||||
<li>
|
||||
Unterkunft: {{ mutableData.items['accommodation'].mutableBefore|date('d.m.Y') }}
|
||||
</li>
|
||||
<li>
|
||||
Teilnehmer: {{ mutableData.items['participant_data'].mutableBefore|date('d.m.Y') }}
|
||||
</li>
|
||||
<li>
|
||||
Zusatzleistungen: {{ mutableData.items['additional_services'].mutableBefore|date('d.m.Y') }}
|
||||
</li>
|
||||
<li>
|
||||
Beförderung: {{ mutableData.items['transportation'].mutableBefore|date('d.m.Y') }}
|
||||
</li>
|
||||
<li>
|
||||
Zustiege: {{ mutableData.items['pickup'].mutableBefore|date('d.m.Y') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% for child in form.participants %}
|
||||
{% set participant = child.vars.data %}
|
||||
<div class="{{ html_classes('p-8 border rounded-md', { 'border-gray-300': child.vars.valid, 'border-red-500': not child.vars.valid }) }}" {{ stimulus_controller('availabilities')|stimulus_controller('toggle', {'open': child.vars.valid == false}, { 'closed': 'hidden' }) }}>
|
||||
<div class="{{ html_classes('p-8 border rounded-md', { 'border-gray-300': child.vars.valid, 'border-red-500': not child.vars.valid }) }}" {{ stimulus_controller('toggle', {'open': child.vars.valid == false}, { 'closed': 'hidden' }) }}>
|
||||
<div role="button" class="flex items-center justify-between" {{ stimulus_action('toggle', 'toggle', 'click') }}>
|
||||
<div class="text-xl font-semibold">Teilnehmer:in {{ participant.index }}: {{ participant.firstName }} {{ participant.lastName }}</div>
|
||||
<div class="text-2xl font-semibold">Teilnehmer:in {{ participant.index }}: {{ participant.firstName }} {{ participant.lastName }}</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" {{ stimulus_target('toggle', 'icon') }}>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="hidden" {{ stimulus_target('toggle', 'toggle') }}>
|
||||
<div class="flex flex-col space-y-4 pt-4">
|
||||
<div>
|
||||
<div class="relative">
|
||||
<h3 class="text-xl font-semibold pb-2">
|
||||
Persönliche Daten
|
||||
</h3>
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-4 pb-4">
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-4 pb-4">
|
||||
{{ form_row(child.firstName) }}
|
||||
{{ form_row(child.lastName) }}
|
||||
{{ form_row(child.gender) }}
|
||||
{{ form_row(child.dateOfBirth) }}
|
||||
{{ form_row(child.nationality) }}
|
||||
</fieldset>
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-4 pb-4">
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-4 pb-4">
|
||||
{{ form_row(child.email) }}
|
||||
{{ form_row(child.mobile) }}
|
||||
</fieldset>
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-4">
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-4">
|
||||
{{ form_row(child.height) }}
|
||||
{{ form_row(child.shoeSize) }}
|
||||
{{ form_row(child.weight) }}
|
||||
</fieldset>
|
||||
{% if not travelData.participantDataMutable %}
|
||||
<div class="absolute top-0 left-0 inset-0 cursor-not-allowed"></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold pb-2">
|
||||
@@ -93,11 +160,11 @@
|
||||
{% set room = bookingData.roomForParticipant(participant.index) %}
|
||||
{{ room.label }} ({{ room.individualPrice[participant.index]|format_currency('EUR') }})
|
||||
</div>
|
||||
<div>
|
||||
<div class="relative">
|
||||
<h3 class="text-xl font-semibold pb-2">
|
||||
Leistungen
|
||||
</h3>
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-4">
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-4">
|
||||
{% if child.courses is defined %}
|
||||
{{ form_row(child.courses) }}
|
||||
{% endif %}
|
||||
@@ -114,12 +181,15 @@
|
||||
{{ form_row(child.rentals) }}
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% if not travelData.additionalServicesMutable %}
|
||||
<div class="absolute top-0 left-0 inset-0 cursor-not-allowed"></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<div class="relative">
|
||||
<h3 class="text-xl font-semibold pb-2">
|
||||
Hin-/Rückreise
|
||||
</h3>
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-4" {{ stimulus_controller('select-toggle', { 'show': ['BUS'] }, { 'hidden': 'hidden' }) }}>
|
||||
<fieldset class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-4" {{ stimulus_controller('select-toggle', { 'show': ['BUS'] }, { 'hidden': 'hidden' }) }}>
|
||||
{{ form_row(child.transportationServiceTo) }}
|
||||
{{ form_row(child.transportationServiceFro) }}
|
||||
{% if child.pickup is defined %}
|
||||
@@ -128,6 +198,9 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% if not travelData.transportationServicesMutable %}
|
||||
<div class="absolute top-0 left-0 inset-0 cursor-not-allowed"></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold pb-2">
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<div class="flex h-6 items-center">
|
||||
{{ form_widget(form) }}
|
||||
</div>
|
||||
<div class="{{ html_classes('ml-3 leading-6', { 'text-red-500': errors|length }) }}">
|
||||
<div class="{{ html_classes('ml-1 leading-6', { 'text-red-500': errors|length }) }}">
|
||||
<label for="{{ form.vars.id }}">
|
||||
{{ form.vars.label }}
|
||||
</label>
|
||||
@@ -125,16 +125,7 @@
|
||||
|
||||
{%- block choice_widget_expanded -%}
|
||||
{%- for child in form %}
|
||||
<div class="relative flex items-start pb-2">
|
||||
<div class="min-w-0 flex-1 text-sm leading-4">
|
||||
<label for="{{ child.vars.id }}" class="select-none font-medium text-gray-900">
|
||||
{{ child.vars.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="ml-3 flex h-4 items-center">
|
||||
{{- form_widget(child) -}}
|
||||
</div>
|
||||
</div>
|
||||
{{- form_row(child) -}}
|
||||
{% endfor -%}
|
||||
{%- endblock choice_widget_expanded -%}
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="content">
|
||||
{% include '_partials/_flashes.html.twig' %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<div class="htmx-indicator fixed top-0 left-0 inset-0 z-10 bg-white/90 flex items-center justify-center"
|
||||
id="loading-indicator">
|
||||
{% include '_partials/_spinner.html.twig' with { 'class': 'w-8 h-8'} %}
|
||||
</div>
|
||||
{% include '_partials/_flashes.html.twig' %}
|
||||
</div>
|
||||
|
||||
@@ -6,40 +6,41 @@
|
||||
<h2 class="text-2xl font-semibold pb-4">
|
||||
Persönliche Daten
|
||||
</h2>
|
||||
{{ form_start(form) }}
|
||||
{{ form_start(personalDataForm) }}
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-4 pb-4">
|
||||
{{ form_row(form.firstName) }}
|
||||
{{ form_row(form.name) }}
|
||||
{{ form_row(form.gender) }}
|
||||
{{ form_row(form.dateOfBirth) }}
|
||||
{{ form_row(personalDataForm.firstName) }}
|
||||
{{ form_row(personalDataForm.name) }}
|
||||
{{ form_row(personalDataForm.gender) }}
|
||||
{{ form_row(personalDataForm.dateOfBirth) }}
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold pb-2">
|
||||
Anschrift
|
||||
</h3>
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-4 pb-4">
|
||||
<div class="col-span-2">
|
||||
{{ form_row(form.street) }}
|
||||
{{ form_row(personalDataForm.street) }}
|
||||
</div>
|
||||
{{ form_row(form.postCode) }}
|
||||
{{ form_row(form.city) }}
|
||||
{{ form_row(form.country) }}
|
||||
{{ form_row(personalDataForm.postCode) }}
|
||||
{{ form_row(personalDataForm.city) }}
|
||||
{{ form_row(personalDataForm.country) }}
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold pb-2">
|
||||
Kontakt
|
||||
</h3>
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-4 pb-4">
|
||||
<div class="col-span-2">
|
||||
{{ form_row(form.email) }}
|
||||
{{ form_row(personalDataForm.email) }}
|
||||
</div>
|
||||
{{ form_row(form.phone) }}
|
||||
{{ form_row(form.mobile) }}
|
||||
{{ form_row(personalDataForm.phone) }}
|
||||
{{ form_row(personalDataForm.mobile) }}
|
||||
</div>
|
||||
<button type="submit" class="btn">
|
||||
Speichern
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{{ form_rest(personalDataForm) }}
|
||||
{{ form_end(personalDataForm) }}
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<h2 class="text-2xl font-semibold pb-4">
|
||||
Newsletter
|
||||
@@ -48,7 +49,7 @@
|
||||
Du bist aktuell {% if not personalData.communication.newsletter %}<strong>nicht</strong> {% endif%} zum Newsletter
|
||||
angemeldet.
|
||||
</p>
|
||||
<button type="button" class="btn" hx-post="{{ path('app_personal_data_newsletter') }}" hx-target="#app">
|
||||
<button type="button" class="btn" hx-post="{{ path('app_personal_data_newsletter') }}" hx-target="#app" hx-swap="outerHTML">
|
||||
{% if personalData.communication.newsletter %}
|
||||
vom Newsletter abmelden
|
||||
{% else %}
|
||||
@@ -57,4 +58,5 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user