wip: transportation services
This commit is contained in:
@@ -393,8 +393,8 @@ class ParticipantParkingFieldHandler extends AbstractParticipantFieldHandler
|
||||
|
||||
private function isParkingApplicable($participant): bool
|
||||
{
|
||||
$outboundIsPkw = $participant->transportationOutbound?->subType === 'PKW';
|
||||
$inboundIsPkw = $participant->transportationInbound?->subType === 'PKW';
|
||||
$outboundIsPkw = DirectionMapper::SUBTYPE_CAR_API === $participant->transportationOutbound?->subType;
|
||||
$inboundIsPkw = DirectionMapper::SUBTYPE_CAR_API === $participant->transportationInbound?->subType;
|
||||
|
||||
return $outboundIsPkw || $inboundIsPkw;
|
||||
}
|
||||
@@ -487,8 +487,8 @@ private function formatTransportationServiceLabel(Service $service): string
|
||||
|
||||
// Add transportation type indicator
|
||||
$typeIndicator = match($service->subType) {
|
||||
'BUS' => '🚌',
|
||||
'PKW' => '🚗',
|
||||
DirectionMapper::SUBTYPE_BUS_API => '🚌',
|
||||
DirectionMapper::SUBTYPE_CAR_API => '🚗',
|
||||
default => ''
|
||||
};
|
||||
|
||||
@@ -514,9 +514,9 @@ private function formatTransportationServiceLabel(Service $service): string
|
||||
|
||||
### Phase 3: Conditional Field States & UX 🎨
|
||||
|
||||
#### 3.1 Transportation Type Condition
|
||||
#### 3.1 Service Sub-Type Condition
|
||||
|
||||
**File:** `src/Form/Service/Condition/TransportationTypeCondition.php`
|
||||
**File:** `src/Form/Service/Condition/ServiceSubTypeCondition.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
@@ -525,20 +525,24 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
* Condition that evaluates transportation service type.
|
||||
* Condition that evaluates service sub-types.
|
||||
*
|
||||
* Used to show/hide fields based on whether transportation
|
||||
* is bus or self-organized (PKW) for specific directions.
|
||||
* This condition enables field state logic based on the sub-type property
|
||||
* of Service objects. It can be used with any service field (transportation,
|
||||
* additional services, etc.) to show/hide or enable/disable dependent fields
|
||||
* based on the selected service type.
|
||||
*/
|
||||
class TransportationTypeCondition implements FieldConditionInterface
|
||||
class ServiceSubTypeCondition implements FieldConditionInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $direction, // 'outbound' or 'inbound'
|
||||
private readonly string $expectedType, // 'BUS' or 'PKW'
|
||||
private readonly string $serviceFieldName,
|
||||
private readonly string $operator,
|
||||
private readonly string|array $expectedSubType,
|
||||
) {}
|
||||
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
@@ -582,35 +586,29 @@ class TransportationTypeCondition implements FieldConditionInterface
|
||||
**Update:** `src/Form/Service/CreateFieldStateProvider.php`
|
||||
|
||||
```php
|
||||
use App\Form\Service\Condition\TransportationTypeCondition;
|
||||
use App\Form\Service\Condition\CompositeCondition;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Service\Condition\ServiceSubTypeCondition;
|
||||
|
||||
protected function registerFieldStateConditions(): void
|
||||
{
|
||||
// ... existing conditions
|
||||
|
||||
// Show outbound pickup only when outbound transportation is BUS
|
||||
// Transportation-related field conditions
|
||||
|
||||
// Hide outbound pickup when transportation is not BUS
|
||||
$this->fieldStateConditions['pickupOutbound'] = [
|
||||
'hidden' => CompositeCondition::not(
|
||||
new TransportationTypeCondition('outbound', 'BUS')
|
||||
),
|
||||
'hidden' => ServiceSubTypeCondition::notEquals('transportationOutbound', DirectionMapper::SUBTYPE_BUS_API),
|
||||
];
|
||||
|
||||
// Show inbound pickup only when inbound transportation is BUS
|
||||
// Hide inbound pickup when transportation is not BUS
|
||||
$this->fieldStateConditions['pickupInbound'] = [
|
||||
'hidden' => CompositeCondition::not(
|
||||
new TransportationTypeCondition('inbound', 'BUS')
|
||||
),
|
||||
'hidden' => ServiceSubTypeCondition::notEquals('transportationInbound', DirectionMapper::SUBTYPE_BUS_API),
|
||||
];
|
||||
|
||||
// Show parking only when at least one direction is PKW
|
||||
// Hide parking when outbound transportation is not PKW (car)
|
||||
// Parking is offered at holiday destination for those arriving by car
|
||||
$this->fieldStateConditions['parking'] = [
|
||||
'hidden' => CompositeCondition::not(
|
||||
CompositeCondition::or(
|
||||
new TransportationTypeCondition('outbound', 'PKW'),
|
||||
new TransportationTypeCondition('inbound', 'PKW')
|
||||
)
|
||||
),
|
||||
'hidden' => ServiceSubTypeCondition::notEquals('transportationOutbound', DirectionMapper::SUBTYPE_CAR_API),
|
||||
];
|
||||
}
|
||||
```
|
||||
@@ -667,15 +665,40 @@ $dynamicFields = [
|
||||
|
||||
#### 4.3 Constants Update
|
||||
|
||||
**Update:** `src/BusProNet/Constants.php`
|
||||
**Update:** `src/BusProNet/Utility/DirectionMapper.php`
|
||||
|
||||
```php
|
||||
// Add parking token if not already defined
|
||||
public const TOKEN_PARKING = 'PAR';
|
||||
// Transportation service sub-types (API format - German abbreviations)
|
||||
public const SUBTYPE_BUS_API = 'BUS';
|
||||
public const SUBTYPE_CAR_API = 'PKW';
|
||||
|
||||
// Transportation subtypes
|
||||
// Transportation service sub-types (internal format - English)
|
||||
public const SUBTYPE_BUS = 'BUS';
|
||||
public const SUBTYPE_PKW = 'PKW';
|
||||
public const SUBTYPE_CAR = 'CAR';
|
||||
|
||||
/**
|
||||
* Maps API transportation sub-type to internal sub-type.
|
||||
*/
|
||||
public static function apiToInternal(string $apiSubType): string
|
||||
{
|
||||
return match ($apiSubType) {
|
||||
self::SUBTYPE_BUS_API => self::SUBTYPE_BUS,
|
||||
self::SUBTYPE_CAR_API => self::SUBTYPE_CAR,
|
||||
default => throw new \InvalidArgumentException("Unknown API sub-type: $apiSubType"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps internal transportation sub-type to API sub-type.
|
||||
*/
|
||||
public static function internalToApi(string $internalSubType): string
|
||||
{
|
||||
return match ($internalSubType) {
|
||||
self::SUBTYPE_BUS => self::SUBTYPE_BUS_API,
|
||||
self::SUBTYPE_CAR => self::SUBTYPE_CAR_API,
|
||||
default => throw new \InvalidArgumentException("Unknown internal sub-type: $internalSubType"),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## UX Design & User Experience 🎯
|
||||
@@ -818,27 +841,62 @@ public const SUBTYPE_PKW = 'PKW';
|
||||
|
||||
### Sprint 1: Foundation (Week 1)
|
||||
- ✅ Create documentation
|
||||
- ✅ Implement DirectionMapper utility
|
||||
- ✅ Implement DirectionMapper utility (removed unused toEnglish method)
|
||||
- ✅ Update ParticipantDto properties
|
||||
- ✅ Update BookingEditDto mapping
|
||||
- ✅ Create transportation field handlers (Outbound/Inbound)
|
||||
- ✅ Create pickup field handlers with conditional logic
|
||||
- ✅ Add parking service handler for self-organized transport
|
||||
- ✅ Add TOKEN_PARKING constant
|
||||
- ✅ Update ParticipantFieldOptionsProvider for transportation services
|
||||
- ✅ Integrate transportation fields into BookingCreateParticipantType
|
||||
- ✅ Create ServiceSubTypeCondition for field state management
|
||||
- ✅ Update field state provider with transportation conditions
|
||||
- ✅ Implement transportation type mapping for API/internal consistency
|
||||
|
||||
### Sprint 2: Core Implementation (Week 2)
|
||||
- Create transportation field handlers
|
||||
- Create pickup field handlers
|
||||
- Add parking service handler
|
||||
- Update field options provider
|
||||
### Sprint 2: Advanced Features (Week 2)
|
||||
- 🚧 Add pricing integration for transportation services
|
||||
- 🚧 Update HTMX integration for real-time transportation updates
|
||||
|
||||
### Sprint 3: UX & Integration (Week 3)
|
||||
- Implement conditional field states
|
||||
- Update form integration
|
||||
- Add transportation service configuration
|
||||
- Implement HTMX real-time updates
|
||||
### Sprint 3: Testing & Deployment (Week 3)
|
||||
- Unit testing for all components
|
||||
- Integration testing for form flow
|
||||
- Manual testing scenarios
|
||||
- Performance optimization
|
||||
|
||||
### Sprint 4: Testing & Polish (Week 4)
|
||||
- Add pricing integration
|
||||
- Comprehensive testing
|
||||
- UX refinements
|
||||
- Documentation updates
|
||||
## Key Implementation Highlights 🌟
|
||||
|
||||
### Transportation Type Mapping System
|
||||
|
||||
**Problem Solved:** BusProNet uses German abbreviations ('PKW') while internal code should use English terminology ('CAR') for consistency.
|
||||
|
||||
**Solution:** Enhanced `DirectionMapper` utility with bidirectional mapping:
|
||||
- **API Format:** `SUBTYPE_CAR_API = 'PKW'`, `SUBTYPE_BUS_API = 'BUS'`
|
||||
- **Internal Format:** `SUBTYPE_CAR = 'CAR'`, `SUBTYPE_BUS = 'BUS'`
|
||||
- **Mapping Methods:** `apiToInternal()`, `internalToApi()`, validation helpers
|
||||
|
||||
### Generic Service Sub-Type Condition
|
||||
|
||||
**Achievement:** Created reusable `ServiceSubTypeCondition` instead of transportation-specific logic:
|
||||
- Supports multiple operators: `equals`, `notEquals`, `in`, `notIn`
|
||||
- Works with any service field, not just transportation
|
||||
- Handles both API and internal sub-type values
|
||||
- Provides static factory methods for common use cases
|
||||
|
||||
### Conditional UX Logic
|
||||
|
||||
**Smart Field Visibility:**
|
||||
- **Pickup Fields:** Only visible when respective transportation is BUS
|
||||
- **Parking Field:** Only visible when outbound transportation is CAR (PKW)
|
||||
- Uses API constants since Service objects contain API values
|
||||
- Proper business logic: parking needed at destination for car arrivals
|
||||
|
||||
### Backward Compatibility
|
||||
|
||||
- Maintained all existing property names with deprecation notices
|
||||
- API integration continues using BusProNet's expected format
|
||||
- Internal code uses clean English naming
|
||||
- Seamless migration path for existing functionality
|
||||
|
||||
## Success Criteria ✅
|
||||
|
||||
|
||||
Reference in New Issue
Block a user