feat: step input for room type selection
This commit is contained in:
@@ -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}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user