feat: step input for room type selection

This commit is contained in:
Björn Fromme
2026-03-16 11:59:11 +01:00
parent b73a02048e
commit c6678d16a6
6 changed files with 104 additions and 18 deletions
@@ -0,0 +1,44 @@
import {Controller} from '@hotwired/stimulus'
export default class extends Controller {
static targets = ['field', 'display', 'buttonInc', 'buttonDec']
static values = {
current: Number,
min: Number,
max: Number
}
connect() {
this.currentValue = parseInt(this.fieldTarget.value) || 0
this.updateForm()
}
increase() {
if (null !== this.maxValue && this.currentValue >= this.maxValue) {
return
}
this.currentValue = this.currentValue + 1
this.updateForm()
}
decrease() {
if (null !== this.minValue && this.currentValue <= this.minValue) {
return
}
this.currentValue = this.currentValue - 1
this.updateForm()
}
updateForm() {
this.displayTarget.innerText = this.currentValue
this.fieldTarget.value = this.currentValue
this.buttonIncTarget.disabled = null !== this.maxValue && this.currentValue >= this.maxValue
this.buttonDecTarget.disabled = null !== this.minValue && this.currentValue <= this.minValue
// Trigger change event to fire HTMX request
this.fieldTarget.dispatchEvent(new Event('change', {bubbles: true}))
}
}
+5 -1
View File
@@ -20,7 +20,7 @@
.bg-button--active, .bg-button--active,
.bg-button--secondary { .bg-button--secondary {
@apply bg-badge-gradient; @apply bg-badge-gradient text-white;
@apply hover:bg-none hover:bg-pink; @apply hover:bg-none hover:bg-pink;
} }
@@ -43,3 +43,7 @@
@apply bg-white; @apply bg-white;
@apply border-secondary text-secondary; @apply border-secondary text-secondary;
} }
.button[disabled] {
@apply cursor-not-allowed;
}
+3 -2
View File
@@ -4,7 +4,6 @@ namespace App\Form;
use App\Form\Model\RoomSelectionDto; use App\Form\Model\RoomSelectionDto;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
@@ -28,9 +27,11 @@ class RoomSelectType extends AbstractType
$label .= sprintf(' (€%s pro Person)', $formattedPrice); $label .= sprintf(' (€%s pro Person)', $formattedPrice);
} }
$form->add('quantity', ChoiceType::class, [ $form->add('quantity', StepSelectChoiceType::class, [
'label' => $label, 'label' => $label,
'required' => true, 'required' => true,
'min_value' => 0,
'max_value' => $data->maxQuantity,
'choices' => ['-' => 0] + array_combine(range(1, $data->maxQuantity), range(1, $data->maxQuantity)), 'choices' => ['-' => 0] + array_combine(range(1, $data->maxQuantity), range(1, $data->maxQuantity)),
]); ]);
}); });
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class StepSelectChoiceType extends AbstractType
{
public function getParent(): string
{
return ChoiceType::class;
}
public function buildView(FormView $view, FormInterface $form, array $options): void
{
$view->vars['min_value'] = $options['min_value'];
$view->vars['max_value'] = $options['max_value'];
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'min_value' => null,
'max_value' => null,
]);
}
}
+2 -15
View File
@@ -13,19 +13,6 @@
{# This block contains the room selection form fields #} {# This block contains the room selection form fields #}
{% block room_selection_form %} {% block room_selection_form %}
<div id="room-selection-form"{% if htmx_oob_swap is defined and htmx_oob_swap %} hx-swap-oob="true"{% endif %}> <div id="room-selection-form"{% if htmx_oob_swap is defined and htmx_oob_swap %} hx-swap-oob="true"{% endif %}>
{% if bookingCreateDto.bookingStatus == 'A' %}
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
<div class="flex items-start">
<svg class="w-5 h-5 text-blue-600 mt-0.5 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
</svg>
<div>
<h3 class="text-blue-900 font-semibold">Buchung auf Anfrage</h3>
<p class="text-blue-800 text-sm mt-1">Diese Reise kann nur auf Anfrage gebucht werden. Deine Buchung wird nach Eingang geprüft und du erhältst eine Bestätigung.</p>
</div>
</div>
</div>
{% endif %}
{{ form_errors(form) }} {{ form_errors(form) }}
{% if groupedRooms.by_room is not empty %} {% if groupedRooms.by_room is not empty %}
<h3> <h3>
@@ -36,7 +23,7 @@
'attr': { 'attr': {
'hx-post': path('app_booking_create_step_1_refresh'), 'hx-post': path('app_booking_create_step_1_refresh'),
'hx-swap': 'none', 'hx-swap': 'none',
'hx-trigger': 'change', 'hx-trigger': 'change delay:300ms',
} }
}) }} }) }}
{% endfor %} {% endfor %}
@@ -50,7 +37,7 @@
'attr': { 'attr': {
'hx-post': path('app_booking_create_step_1_refresh'), 'hx-post': path('app_booking_create_step_1_refresh'),
'hx-swap': 'none', 'hx-swap': 'none',
'hx-trigger': 'change' 'hx-trigger': 'change delay:300ms'
} }
}) }} }) }}
{% endfor %} {% endfor %}
+19
View File
@@ -318,3 +318,22 @@
{{- parent() -}} {{- parent() -}}
{%- endif -%} {%- endif -%}
{% endblock %} {% endblock %}
{% block step_select_choice_widget %}
<div {{ stimulus_controller('step-input', { 'min': form.vars.min_value, 'max': form.vars.max_value }) }}>
{{ form_widget(form, { 'attr': {
'data-step-input-target': 'field',
'class': 'hidden',
} }) }}
<div class="flex space-x-2">
<button type="button" class="button button--small bg-button" {{ stimulus_target('step-input', 'buttonDec') }} {{ stimulus_action('step-input', 'decrease') }}>
-
</button>
<div {{ stimulus_target('step-input', 'display') }}></div>
<button type="button" class="button button--small bg-button--secondary" {{ stimulus_target('step-input', 'buttonInc') }} {{ stimulus_action('step-input', 'increase') }}>
+
</button>
</div>
</div>
{% endblock %}