feat: remove room selection field entirely for single room type
addresses #869axcukn
This commit is contained in:
@@ -85,7 +85,8 @@ The room assignment system allows participants to freely select any room from St
|
||||
|
||||
**Key Features:**
|
||||
- **Flexible Selection**: All rooms from Step 1 always appear in participant dropdown, regardless of current capacity
|
||||
- **Auto-Assignment**: When exactly ONE room type selected, all participants auto-assigned and dropdown disabled
|
||||
- **Auto-Assignment**: When exactly ONE room type selected, all participants auto-assigned via `RoomAssignmentService`
|
||||
- **Field Display**: Single room type hides dropdown field entirely, displays room label as read-only text
|
||||
- **Conflict Resolution**: When participant selects room at capacity, system automatically unassigns minimum participants needed
|
||||
- **Unassignment Priority**: Highest index participants unassigned first (keeps applicant and early participants stable)
|
||||
- **User Notifications**: Unassigned participants receive warning notifications via toast system
|
||||
@@ -96,8 +97,11 @@ The room assignment system allows participants to freely select any room from St
|
||||
- `detectRoomCapacityConflict()`: Calculates if assignment would exceed capacity
|
||||
- `resolveRoomCapacityConflict()`: Unassigns minimum participants (highest index first)
|
||||
- Generates notifications for unassigned participants only (not for user-initiated assignment)
|
||||
- `RoomAssignmentService`: Unchanged - still auto-assigns when single room type selected
|
||||
- `ParticipantFieldOptionsProvider`: Unchanged - disables dropdown when single room type (auto-assigned)
|
||||
- Handles missing field gracefully when single room type (field excluded from form)
|
||||
- `RoomAssignmentService`: Auto-assigns when single room type selected at DTO level
|
||||
- `CreateFieldStateProvider`: Hides `assignedRoomId` field when `SingleRoomTypeCondition` is true
|
||||
- `BookingDto::getSingleRoomLabel()`: Returns room label for template display when single room type
|
||||
- Template: Conditionally renders dropdown (multiple rooms) or read-only label (single room)
|
||||
|
||||
**Example Scenario:**
|
||||
- User selects 1x "Doppelzimmer" (capacity 2), 3 participants
|
||||
@@ -248,6 +252,13 @@ Critical for correct pricing and auto-reassignment:
|
||||
- `BulkInsuranceBookingCondition` hides dependent participant insurance fields
|
||||
- Uses `InsuranceService::batchAssignInsuranceToParticipants()` for price tier matching
|
||||
|
||||
### Room Assignment Field
|
||||
- **Single room type**: Field completely hidden, room label displayed as read-only text
|
||||
- **Multiple room types**: Dropdown shown with all selected rooms from Step 1
|
||||
- `SingleRoomTypeCondition` controls field visibility in `CreateFieldStateProvider`
|
||||
- Auto-assignment by `RoomAssignmentService` happens at DTO level, independent of form field
|
||||
- Field handler gracefully handles missing field when excluded from form
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Create Flow
|
||||
|
||||
@@ -151,6 +151,30 @@ class BookingDto
|
||||
return null !== $this->booking && 'O' === $this->booking->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label of the single selected room type.
|
||||
*
|
||||
* Returns the room label when exactly one room type is selected (auto-assignment scenario).
|
||||
* Used by templates to display the room assignment when the dropdown is hidden.
|
||||
*
|
||||
* @return string|null The room label or null if not a single room type scenario
|
||||
*/
|
||||
public function getSingleRoomLabel(): ?string
|
||||
{
|
||||
$selectedRooms = $this->getSelectedRooms();
|
||||
|
||||
// Only return label when exactly one room type selected
|
||||
if (1 !== count($selectedRooms)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$roomSelection = reset($selectedRooms);
|
||||
$availableRooms = $this->travel->getAvailableRooms();
|
||||
$room = $availableRooms[$roomSelection->roomId] ?? null;
|
||||
|
||||
return $room?->label;
|
||||
}
|
||||
|
||||
#[Assert\Callback(callback: 'validateRoomSelection', groups: ['booking_create_step_1'])]
|
||||
public function validateRoomSelection(ExecutionContextInterface $context): void
|
||||
{
|
||||
|
||||
@@ -205,9 +205,9 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider
|
||||
'required' => new ApplicantCondition(),
|
||||
];
|
||||
|
||||
// Make assignedRoomId readonly when only one room type is selected
|
||||
// Hide assignedRoomId field when only one room type is selected (auto-assigned by RoomAssignmentService)
|
||||
$this->fieldStateConditions['assignedRoomId'] = [
|
||||
'readonly' => new SingleRoomTypeCondition(),
|
||||
'hidden' => new SingleRoomTypeCondition(),
|
||||
];
|
||||
|
||||
// Example field state conditions would be registered here
|
||||
|
||||
@@ -85,14 +85,22 @@
|
||||
|
||||
{# Room assignment #}
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
{{ form_row(form.assignedRoomId, {
|
||||
'attr': {
|
||||
'hx-trigger': 'change',
|
||||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||||
'hx-target': '#main-content',
|
||||
'hx-swap': 'innerHTML'
|
||||
}
|
||||
}) }}
|
||||
{% if form.assignedRoomId is defined %}
|
||||
{{ form_row(form.assignedRoomId, {
|
||||
'attr': {
|
||||
'hx-trigger': 'change',
|
||||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||||
'hx-target': '#main-content',
|
||||
'hx-swap': 'innerHTML'
|
||||
}
|
||||
}) }}
|
||||
{% else %}
|
||||
{# Display room label directly when only one room type selected (auto-assigned) #}
|
||||
<div>
|
||||
<label class="font-semibold mb-1 block">Zimmer</label>
|
||||
<div class="text-sm text-gray-600">{{ bookingDto.singleRoomLabel }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if form.remarksRoom is defined %}
|
||||
{{ form_row(form.remarksRoom) }}
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user