| 1 |
<?php |
| 2 |
/** |
| 3 |
* Read-only presentation data for the Setup Wizard module. |
| 4 |
* |
| 5 |
* @package Booking Calendar |
| 6 |
*/ |
| 7 |
|
| 8 |
if ( ! defined( 'ABSPATH' ) ) { |
| 9 |
exit; |
| 10 |
} |
| 11 |
|
| 12 |
/** |
| 13 |
* Provide source-backed initial values and allow-listed field choices. |
| 14 |
* |
| 15 |
* This service reads current WordPress and Booking Calendar configuration but |
| 16 |
* never writes it. Canonical-backed fields are refreshed whenever a step is |
| 17 |
* opened; saved checkpoint values remain authoritative only for wizard-owned |
| 18 |
* metadata that cannot be reconstructed safely from plugin state. |
| 19 |
*/ |
| 20 |
final class WPBC_Setup_Wizard_Step_Data { |
| 21 |
|
| 22 |
/** @var WPBC_Setup_Wizard_Step_Module_Registry */ |
| 23 |
private $module_registry; |
| 24 |
|
| 25 |
/** |
| 26 |
* Build the read-only provider around the reusable module registry. |
| 27 |
* |
| 28 |
* @param WPBC_Setup_Wizard_Step_Module_Registry|null $module_registry Optional shared module registry. |
| 29 |
*/ |
| 30 |
public function __construct( $module_registry = null ) { |
| 31 |
$this->module_registry = $module_registry instanceof WPBC_Setup_Wizard_Step_Module_Registry |
| 32 |
? $module_registry |
| 33 |
: new WPBC_Setup_Wizard_Step_Module_Registry(); |
| 34 |
} |
| 35 |
|
| 36 |
/** |
| 37 |
* Return the presentation contract for one registered step. |
| 38 |
* |
| 39 |
* @param string $step_id Registered step identifier. |
| 40 |
* @param array<string,mixed> $draft_values Normalized draft values keyed by step. |
| 41 |
* @param array<string,mixed> $step_results Verified progressive-save results keyed by step. |
| 42 |
* |
| 43 |
* @return array<string,mixed> Authorized template context for the step. |
| 44 |
*/ |
| 45 |
public function get_step_context( $step_id, array $draft_values, array $step_results = array() ) { |
| 46 |
$step_values = $this->get_step_values( $step_id, $draft_values ); |
| 47 |
$step_module = $this->module_registry->get_module( $step_id ); |
| 48 |
|
| 49 |
if ( null !== $step_module ) { |
| 50 |
$consumer_context = array(); |
| 51 |
foreach ( $step_module->get_context_dependencies() as $dependency_step_id ) { |
| 52 |
$consumer_context[ $dependency_step_id ] = $this->get_step_values( $dependency_step_id, $draft_values ); |
| 53 |
} |
| 54 |
$consumer_context['_step_state'] = array( |
| 55 |
'has_saved_values' => isset( $draft_values[ $step_id ] ) |
| 56 |
&& is_array( $draft_values[ $step_id ] ) |
| 57 |
&& ! empty( $draft_values[ $step_id ] ), |
| 58 |
); |
| 59 |
if ( ! empty( $step_results ) ) { |
| 60 |
$consumer_context['_step_results'] = $step_results; |
| 61 |
} |
| 62 |
|
| 63 |
return $step_module->get_context( $step_values, $consumer_context ); |
| 64 |
} |
| 65 |
|
| 66 |
switch ( $step_id ) { |
| 67 |
case 'business_details': |
| 68 |
return array( |
| 69 |
'values' => $step_values, |
| 70 |
'business_stages' => $this->get_business_stages(), |
| 71 |
'industries' => $this->get_industry_groups(), |
| 72 |
); |
| 73 |
|
| 74 |
case 'booking_experience': |
| 75 |
return array( |
| 76 |
'values' => $step_values, |
| 77 |
'experiences' => $this->get_booking_experiences(), |
| 78 |
); |
| 79 |
|
| 80 |
case 'customer_journey': |
| 81 |
$selected_booking_mode_id = $this->get_selected_booking_mode_id( $draft_values ); |
| 82 |
$journey_groups = $this->get_customer_journey_groups( $selected_booking_mode_id ); |
| 83 |
$ordered_journeys = array(); |
| 84 |
|
| 85 |
foreach ( $journey_groups as $journey_group ) { |
| 86 |
foreach ( $journey_group['journeys'] as $journey_id => $journey ) { |
| 87 |
$ordered_journeys[ $journey_id ] = $journey; |
| 88 |
} |
| 89 |
} |
| 90 |
|
| 91 |
return array( |
| 92 |
'values' => $step_values, |
| 93 |
'selected_booking_mode_id' => $selected_booking_mode_id, |
| 94 |
'journey_groups' => $journey_groups, |
| 95 |
'journeys' => $ordered_journeys, |
| 96 |
); |
| 97 |
|
| 98 |
default: |
| 99 |
return array( 'values' => array() ); |
| 100 |
} |
| 101 |
} |
| 102 |
|
| 103 |
/** |
| 104 |
* Resolve current presentation values for one step. |
| 105 |
* |
| 106 |
* Checkpoint values normally preserve prior wizard choices. A module may mark |
| 107 |
* allow-listed fields as current-value fields, in which case the latest |
| 108 |
* server-side initial value is overlaid after the checkpoint merge. This |
| 109 |
* prevents Back, rail navigation, and the review page from presenting stale |
| 110 |
* copies of canonical settings changed outside the wizard. |
| 111 |
* |
| 112 |
* @param string $step_id Registered step identifier. |
| 113 |
* @param array<string,mixed> $draft_values Normalized draft values keyed by step. |
| 114 |
* |
| 115 |
* @return array<string,mixed> Values ready for the step template. |
| 116 |
*/ |
| 117 |
public function get_step_values( $step_id, array $draft_values ) { |
| 118 |
$initial_values = $this->get_initial_values( $step_id ); |
| 119 |
$saved_values = isset( $draft_values[ $step_id ] ) && is_array( $draft_values[ $step_id ] ) |
| 120 |
? $draft_values[ $step_id ] |
| 121 |
: array(); |
| 122 |
$step_values = array_merge( $initial_values, array_intersect_key( $saved_values, $initial_values ) ); |
| 123 |
$current_fields = array_intersect( |
| 124 |
$this->get_current_value_field_names( $step_id ), |
| 125 |
array_keys( $initial_values ) |
| 126 |
); |
| 127 |
|
| 128 |
if ( empty( $current_fields ) ) { |
| 129 |
return $step_values; |
| 130 |
} |
| 131 |
|
| 132 |
return array_merge( |
| 133 |
$step_values, |
| 134 |
array_intersect_key( $initial_values, array_flip( $current_fields ) ) |
| 135 |
); |
| 136 |
} |
| 137 |
|
| 138 |
/** |
| 139 |
* Return fields whose current server values override checkpoint snapshots. |
| 140 |
* |
| 141 |
* Booking Mode is the only core step backed directly by canonical plugin |
| 142 |
* state. Reusable domain modules opt in through the companion current-values |
| 143 |
* contract. Business Details and Customer Journey remain wizard metadata. |
| 144 |
* |
| 145 |
* @param string $step_id Registered step identifier. |
| 146 |
* |
| 147 |
* @return string[] Current-value field identifiers. |
| 148 |
*/ |
| 149 |
public function get_current_value_field_names( $step_id ) { |
| 150 |
$step_module = $this->module_registry->get_module( $step_id ); |
| 151 |
if ( $step_module instanceof WPBC_Setup_Wizard_Current_Values_Module ) { |
| 152 |
return array_values( |
| 153 |
array_intersect( |
| 154 |
$step_module->get_current_value_field_names(), |
| 155 |
$step_module->get_field_names() |
| 156 |
) |
| 157 |
); |
| 158 |
} |
| 159 |
|
| 160 |
return 'booking_experience' === $step_id ? array( 'booking_experience' ) : array(); |
| 161 |
} |
| 162 |
|
| 163 |
/** |
| 164 |
* Return current canonical values or source-supported suggestions for a step. |
| 165 |
* |
| 166 |
* No value returned here is persisted unless the user explicitly continues |
| 167 |
* or goes back from the corresponding Setup Wizard step. |
| 168 |
* |
| 169 |
* @param string $step_id Registered step identifier. |
| 170 |
* |
| 171 |
* @return array<string,mixed> Read-only initial values. |
| 172 |
*/ |
| 173 |
public function get_initial_values( $step_id ) { |
| 174 |
$step_module = $this->module_registry->get_module( $step_id ); |
| 175 |
if ( null !== $step_module ) { |
| 176 |
return $step_module->get_initial_values(); |
| 177 |
} |
| 178 |
|
| 179 |
switch ( $step_id ) { |
| 180 |
case 'business_details': |
| 181 |
return array( |
| 182 |
'business_name' => sanitize_text_field( (string) get_option( 'blogname', '' ) ), |
| 183 |
'business_stage' => 'starting', |
| 184 |
'industry' => 'other', |
| 185 |
'booking_email' => sanitize_email( (string) get_option( 'admin_email', '' ) ), |
| 186 |
'personalization_consent' => true, |
| 187 |
); |
| 188 |
|
| 189 |
case 'booking_experience': |
| 190 |
$selected_mode_id = function_exists( 'wpbc_booking_modes_get_selected_mode_id' ) |
| 191 |
? wpbc_booking_modes_get_selected_mode_id() |
| 192 |
: 'classic'; |
| 193 |
$available_mode_ids = array_column( $this->get_booking_experiences(), 'id' ); |
| 194 |
$fallback_mode_id = in_array( 'classic', $available_mode_ids, true ) |
| 195 |
? 'classic' |
| 196 |
: ( ! empty( $available_mode_ids ) ? (string) reset( $available_mode_ids ) : 'classic' ); |
| 197 |
|
| 198 |
return array( |
| 199 |
'booking_experience' => in_array( $selected_mode_id, $available_mode_ids, true ) ? $selected_mode_id : $fallback_mode_id, |
| 200 |
); |
| 201 |
|
| 202 |
case 'customer_journey': |
| 203 |
return array( |
| 204 |
'customer_journey' => '', |
| 205 |
); |
| 206 |
|
| 207 |
default: |
| 208 |
return array(); |
| 209 |
} |
| 210 |
} |
| 211 |
|
| 212 |
/** |
| 213 |
* Return field names accepted for one step. |
| 214 |
* |
| 215 |
* @param string $step_id Registered step identifier. |
| 216 |
* |
| 217 |
* @return string[] Ordered allow-list of draft field names. |
| 218 |
*/ |
| 219 |
public function get_field_names( $step_id ) { |
| 220 |
$step_module = $this->module_registry->get_module( $step_id ); |
| 221 |
if ( null !== $step_module ) { |
| 222 |
return $step_module->get_field_names(); |
| 223 |
} |
| 224 |
|
| 225 |
$field_names = array( |
| 226 |
'welcome' => array(), |
| 227 |
'business_details' => array( 'business_name', 'business_stage', 'industry', 'booking_email', 'personalization_consent' ), |
| 228 |
'booking_experience' => array( 'booking_experience' ), |
| 229 |
'customer_journey' => array( 'customer_journey' ), |
| 230 |
); |
| 231 |
|
| 232 |
return isset( $field_names[ $step_id ] ) ? $field_names[ $step_id ] : array(); |
| 233 |
} |
| 234 |
|
| 235 |
/** |
| 236 |
* Return allowed scalar choices for one select, radio, or checkbox field. |
| 237 |
* |
| 238 |
* @param string $step_id Registered step identifier. |
| 239 |
* @param string $field_id Stable field identifier. |
| 240 |
* |
| 241 |
* @return array<int,string|bool> Allowed values, or an empty array for free text. |
| 242 |
*/ |
| 243 |
public function get_allowed_values( $step_id, $field_id ) { |
| 244 |
$available_customer_journey_ids = array(); |
| 245 |
foreach ( array_keys( self::get_customer_journeys() ) as $customer_journey_id ) { |
| 246 |
if ( WPBC_Setup_Wizard_Customer_Journey_Policy::is_journey_available( $customer_journey_id ) ) { |
| 247 |
$available_customer_journey_ids[] = $customer_journey_id; |
| 248 |
} |
| 249 |
} |
| 250 |
|
| 251 |
$field_choices = array( |
| 252 |
'business_details' => array( |
| 253 |
'business_stage' => array_keys( $this->get_business_stages() ), |
| 254 |
'industry' => array_keys( $this->get_industry_options() ), |
| 255 |
'personalization_consent' => array( false, true ), |
| 256 |
), |
| 257 |
'booking_experience' => array( |
| 258 |
'booking_experience' => array_column( $this->get_booking_experiences(), 'id' ), |
| 259 |
), |
| 260 |
'customer_journey' => array( |
| 261 |
'customer_journey' => $available_customer_journey_ids, |
| 262 |
), |
| 263 |
); |
| 264 |
|
| 265 |
return isset( $field_choices[ $step_id ][ $field_id ] ) ? $field_choices[ $step_id ][ $field_id ] : array(); |
| 266 |
} |
| 267 |
|
| 268 |
/** |
| 269 |
* Return required fields for forward navigation from one step. |
| 270 |
* |
| 271 |
* @param string $step_id Registered step identifier. |
| 272 |
* |
| 273 |
* @return string[] Required field identifiers. |
| 274 |
*/ |
| 275 |
public function get_required_fields( $step_id ) { |
| 276 |
$step_module = $this->module_registry->get_module( $step_id ); |
| 277 |
if ( null !== $step_module ) { |
| 278 |
return $step_module->get_required_fields(); |
| 279 |
} |
| 280 |
|
| 281 |
$required_fields = array( |
| 282 |
'business_details' => array( 'business_name', 'business_stage', 'industry', 'booking_email' ), |
| 283 |
'booking_experience' => array( 'booking_experience' ), |
| 284 |
'customer_journey' => array( 'customer_journey' ), |
| 285 |
); |
| 286 |
|
| 287 |
return isset( $required_fields[ $step_id ] ) ? $required_fields[ $step_id ] : array(); |
| 288 |
} |
| 289 |
|
| 290 |
/** |
| 291 |
* Determine whether a step delegates its complete field contract to a module. |
| 292 |
* |
| 293 |
* @param string $step_id Registered step identifier. |
| 294 |
* |
| 295 |
* @return bool True when a reusable module owns the step. |
| 296 |
*/ |
| 297 |
public function is_module_step( $step_id ) { |
| 298 |
return null !== $this->module_registry->get_module( $step_id ); |
| 299 |
} |
| 300 |
|
| 301 |
/** |
| 302 |
* Validate one field through its registered reusable module. |
| 303 |
* |
| 304 |
* @param string $step_id Registered step identifier. |
| 305 |
* @param string $field_id Stable field identifier. |
| 306 |
* @param mixed $raw_value Untrusted submitted or stored value. |
| 307 |
* @param bool $is_required Whether an empty value is invalid. |
| 308 |
* |
| 309 |
* @return mixed|WP_Error Normalized field value or an error. |
| 310 |
*/ |
| 311 |
public function validate_module_field( $step_id, $field_id, $raw_value, $is_required ) { |
| 312 |
$step_module = $this->module_registry->get_module( $step_id ); |
| 313 |
|
| 314 |
if ( null === $step_module ) { |
| 315 |
return new WP_Error( 'wpbc_setup_wizard_step_module_missing', __( 'The requested setup editor is not registered.', 'booking' ) ); |
| 316 |
} |
| 317 |
|
| 318 |
return $step_module->validate_field( $field_id, $raw_value, $is_required ); |
| 319 |
} |
| 320 |
|
| 321 |
/** |
| 322 |
* Validate relationships across a reusable module's normalized fields. |
| 323 |
* |
| 324 |
* Modules without the companion values-validator contract keep the existing |
| 325 |
* field-only behavior. The consumer remains domain-neutral and does not know |
| 326 |
* which field relationships a module owns. |
| 327 |
* |
| 328 |
* @param string $step_id Registered step identifier. |
| 329 |
* @param array<string,mixed> $validated_values Values after individual field validation. |
| 330 |
* @param array<string,mixed> $draft_values Normalized draft values keyed by step. |
| 331 |
* |
| 332 |
* @return array<string,mixed>|WP_Error Validated values or a module-owned error. |
| 333 |
*/ |
| 334 |
public function validate_module_values( $step_id, array $validated_values, array $draft_values = array() ) { |
| 335 |
$step_module = $this->module_registry->get_module( $step_id ); |
| 336 |
|
| 337 |
if ( null === $step_module ) { |
| 338 |
return new WP_Error( 'wpbc_setup_wizard_step_module_missing', __( 'The requested setup editor is not registered.', 'booking' ) ); |
| 339 |
} |
| 340 |
|
| 341 |
if ( $step_module instanceof WPBC_Setup_Wizard_Step_Values_Validator ) { |
| 342 |
$validated_values = $step_module->validate_values( $validated_values ); |
| 343 |
if ( is_wp_error( $validated_values ) ) { |
| 344 |
return $validated_values; |
| 345 |
} |
| 346 |
} |
| 347 |
|
| 348 |
if ( ! $step_module instanceof WPBC_Setup_Wizard_Contextual_Values_Validator ) { |
| 349 |
return $validated_values; |
| 350 |
} |
| 351 |
|
| 352 |
$consumer_context = array(); |
| 353 |
foreach ( $step_module->get_context_dependencies() as $dependency_step_id ) { |
| 354 |
$consumer_context[ $dependency_step_id ] = $this->get_step_values( $dependency_step_id, $draft_values ); |
| 355 |
} |
| 356 |
|
| 357 |
return $step_module->validate_values_with_context( $validated_values, $consumer_context ); |
| 358 |
} |
| 359 |
|
| 360 |
/** |
| 361 |
* Return business maturity options retained from the released wizard. |
| 362 |
* |
| 363 |
* @return array<string,string> Labels keyed by stable draft value. |
| 364 |
*/ |
| 365 |
public function get_business_stages() { |
| 366 |
return array( |
| 367 |
'starting' => __( "I'm just starting my business", 'booking' ), |
| 368 |
'in_business' => __( "I'm already in business", 'booking' ), |
| 369 |
'setup_for_client' => __( "I'm setting up a plugin for a client", 'booking' ), |
| 370 |
); |
| 371 |
} |
| 372 |
|
| 373 |
/** |
| 374 |
* Return grouped industry choices retained from the released wizard. |
| 375 |
* |
| 376 |
* @return array<int,array{label:string,options:array<string,string>}> Industry groups. |
| 377 |
*/ |
| 378 |
public function get_industry_groups() { |
| 379 |
return array( |
| 380 |
array( |
| 381 |
'label' => __( 'Hospitality & Property', 'booking' ), |
| 382 |
'options' => $this->translate_options( |
| 383 |
array( |
| 384 |
'property_rentals' => 'Property Rentals', |
| 385 |
'hotels' => 'Hotels', |
| 386 |
'resorts' => 'Resorts', |
| 387 |
'rentals' => 'Vacation Rentals', |
| 388 |
'coworking_spaces' => 'Coworking Spaces', |
| 389 |
'other_hospitality' => 'Other', |
| 390 |
) |
| 391 |
), |
| 392 |
), |
| 393 |
array( |
| 394 |
'label' => __( 'Transportation & Activities', 'booking' ), |
| 395 |
'options' => $this->translate_options( |
| 396 |
array( |
| 397 |
'car_rentals' => 'Car Rentals', |
| 398 |
'shuttles' => 'Shuttle Services', |
| 399 |
'limousines' => 'Limousines', |
| 400 |
'tour_operators' => 'Tour Operators', |
| 401 |
'adventure_parks' => 'Adventure Parks', |
| 402 |
'other_transportation' => 'Other', |
| 403 |
) |
| 404 |
), |
| 405 |
), |
| 406 |
array( |
| 407 |
'label' => __( 'Events & Entertainment', 'booking' ), |
| 408 |
'options' => $this->translate_options( |
| 409 |
array( |
| 410 |
'event_venues' => 'Event Venues', |
| 411 |
'meeting_rooms' => 'Meeting Rooms', |
| 412 |
'escape_rooms' => 'Escape Rooms', |
| 413 |
'other_events' => 'Other', |
| 414 |
) |
| 415 |
), |
| 416 |
), |
| 417 |
array( |
| 418 |
'label' => __( 'Health & Wellness', 'booking' ), |
| 419 |
'options' => $this->translate_options( |
| 420 |
array( |
| 421 |
'spas' => 'Spas', |
| 422 |
'salons' => 'Salons', |
| 423 |
'clinics' => 'Clinics', |
| 424 |
'massage_therapy' => 'Massage Therapy', |
| 425 |
'other_health' => 'Other', |
| 426 |
) |
| 427 |
), |
| 428 |
), |
| 429 |
array( |
| 430 |
'label' => __( 'Healthcare', 'booking' ), |
| 431 |
'options' => $this->translate_options( |
| 432 |
array( |
| 433 |
'doctors' => 'Doctors', |
| 434 |
'dentists' => 'Dentists', |
| 435 |
'therapists' => 'Therapists', |
| 436 |
'chiropractors' => 'Chiropractors', |
| 437 |
'acupuncturists' => 'Acupuncturists', |
| 438 |
'other_healthcare' => 'Other', |
| 439 |
) |
| 440 |
), |
| 441 |
), |
| 442 |
array( |
| 443 |
'label' => __( 'Professional Services', 'booking' ), |
| 444 |
'options' => $this->translate_options( |
| 445 |
array( |
| 446 |
'consultants' => 'Consultants', |
| 447 |
'lawyers' => 'Lawyers', |
| 448 |
'accountants' => 'Accountants', |
| 449 |
'financial_advisors' => 'Financial Advisors', |
| 450 |
'other_professional' => 'Other', |
| 451 |
) |
| 452 |
), |
| 453 |
), |
| 454 |
array( |
| 455 |
'label' => __( 'Retail & E-Commerce', 'booking' ), |
| 456 |
'options' => $this->translate_options( |
| 457 |
array( |
| 458 |
'product_demos' => 'Product Demonstrations', |
| 459 |
'fitting_appointments' => 'Fitting Appointments', |
| 460 |
'equipment_rentals' => 'Equipment Rentals', |
| 461 |
'other_retail' => 'Other', |
| 462 |
) |
| 463 |
), |
| 464 |
), |
| 465 |
array( |
| 466 |
'label' => __( 'Education & Training', 'booking' ), |
| 467 |
'options' => $this->translate_options( |
| 468 |
array( |
| 469 |
'tutors' => 'Tutors', |
| 470 |
'classes' => 'Classes', |
| 471 |
'schools' => 'Schools', |
| 472 |
'workshops' => 'Workshops', |
| 473 |
'other_education' => 'Other', |
| 474 |
) |
| 475 |
), |
| 476 |
), |
| 477 |
array( |
| 478 |
'label' => __( 'Food & Restaurants', 'booking' ), |
| 479 |
'options' => $this->translate_options( |
| 480 |
array( |
| 481 |
'table_reservations' => 'Table Reservations', |
| 482 |
'catering' => 'Catering', |
| 483 |
'banquet_halls' => 'Banquet Halls', |
| 484 |
'other_food' => 'Other', |
| 485 |
) |
| 486 |
), |
| 487 |
), |
| 488 |
array( |
| 489 |
'label' => __( 'Creative & Technical Services', 'booking' ), |
| 490 |
'options' => $this->translate_options( |
| 491 |
array( |
| 492 |
'photographers' => 'Photographers', |
| 493 |
'videographers' => 'Videographers', |
| 494 |
'web_developers' => 'Web Developers', |
| 495 |
'it_support' => 'IT Support', |
| 496 |
'art_studios' => 'Art Studios', |
| 497 |
'music_lessons' => 'Music Lessons', |
| 498 |
'other_creative' => 'Other', |
| 499 |
) |
| 500 |
), |
| 501 |
), |
| 502 |
array( |
| 503 |
'label' => __( 'Legal Services', 'booking' ), |
| 504 |
'options' => $this->translate_options( |
| 505 |
array( |
| 506 |
'attorneys' => 'Attorneys', |
| 507 |
'legal_consultants' => 'Legal Consultants', |
| 508 |
'other_legal' => 'Other', |
| 509 |
) |
| 510 |
), |
| 511 |
), |
| 512 |
array( |
| 513 |
'label' => __( 'Childcare & Home Services', 'booking' ), |
| 514 |
'options' => $this->translate_options( |
| 515 |
array( |
| 516 |
'daycares' => 'Daycares', |
| 517 |
'babysitting' => 'Babysitting', |
| 518 |
'nanny_services' => 'Nanny Services', |
| 519 |
'cleaners' => 'Cleaners', |
| 520 |
'repairs' => 'Repair Services', |
| 521 |
'handyman' => 'Handyman', |
| 522 |
'other_childcare' => 'Other', |
| 523 |
) |
| 524 |
), |
| 525 |
), |
| 526 |
array( |
| 527 |
'label' => __( 'Sports & Fitness', 'booking' ), |
| 528 |
'options' => $this->translate_options( |
| 529 |
array( |
| 530 |
'gyms' => 'Gyms', |
| 531 |
'fitness_classes' => 'Fitness Classes', |
| 532 |
'yoga_studios' => 'Yoga Studios', |
| 533 |
'golf_lessons' => 'Golf Lessons', |
| 534 |
'sports_equipment_rentals' => 'Sports Equipment Rentals', |
| 535 |
'other_sports' => 'Other', |
| 536 |
) |
| 537 |
), |
| 538 |
), |
| 539 |
array( |
| 540 |
'label' => __( 'Pet Services', 'booking' ), |
| 541 |
'options' => $this->translate_options( |
| 542 |
array( |
| 543 |
'veterinarians' => 'Veterinarians', |
| 544 |
'groomers' => 'Pet Groomers', |
| 545 |
'pet_sitting' => 'Pet Sitting', |
| 546 |
'other_pet' => 'Other', |
| 547 |
) |
| 548 |
), |
| 549 |
), |
| 550 |
array( |
| 551 |
'label' => __( 'Personal Services', 'booking' ), |
| 552 |
'options' => $this->translate_options( |
| 553 |
array( |
| 554 |
'coaching' => 'Coaching', |
| 555 |
'counseling' => 'Counseling', |
| 556 |
'spiritual_services' => 'Spiritual Services', |
| 557 |
'interior_design' => 'Interior Design', |
| 558 |
'other_personal' => 'Other', |
| 559 |
) |
| 560 |
), |
| 561 |
), |
| 562 |
array( |
| 563 |
'label' => __( 'Other', 'booking' ), |
| 564 |
'options' => $this->translate_options( array( 'other' => 'Other' ) ), |
| 565 |
), |
| 566 |
); |
| 567 |
} |
| 568 |
|
| 569 |
/** |
| 570 |
* Flatten grouped industries for validation. |
| 571 |
* |
| 572 |
* @return array<string,string> Industry labels keyed by stable identifier. |
| 573 |
*/ |
| 574 |
private function get_industry_options() { |
| 575 |
$industry_options = array(); |
| 576 |
|
| 577 |
foreach ( $this->get_industry_groups() as $industry_group ) { |
| 578 |
$industry_options = array_merge( $industry_options, $industry_group['options'] ); |
| 579 |
} |
| 580 |
|
| 581 |
return $industry_options; |
| 582 |
} |
| 583 |
|
| 584 |
/** |
| 585 |
* Translate a compact source-defined option map. |
| 586 |
* |
| 587 |
* @param array<string,string> $options English labels keyed by stable identifiers. |
| 588 |
* |
| 589 |
* @return array<string,string> Translated labels keyed by stable identifiers. |
| 590 |
*/ |
| 591 |
private function translate_options( array $options ) { |
| 592 |
$translated_labels = array( |
| 593 |
'Property Rentals' => __( 'Property Rentals', 'booking' ), |
| 594 |
'Hotels' => __( 'Hotels', 'booking' ), |
| 595 |
'Resorts' => __( 'Resorts', 'booking' ), |
| 596 |
'Vacation Rentals' => __( 'Vacation Rentals', 'booking' ), |
| 597 |
'Coworking Spaces' => __( 'Coworking Spaces', 'booking' ), |
| 598 |
'Other' => __( 'Other', 'booking' ), |
| 599 |
'Car Rentals' => __( 'Car Rentals', 'booking' ), |
| 600 |
'Shuttle Services' => __( 'Shuttle Services', 'booking' ), |
| 601 |
'Limousines' => __( 'Limousines', 'booking' ), |
| 602 |
'Tour Operators' => __( 'Tour Operators', 'booking' ), |
| 603 |
'Adventure Parks' => __( 'Adventure Parks', 'booking' ), |
| 604 |
'Event Venues' => __( 'Event Venues', 'booking' ), |
| 605 |
'Meeting Rooms' => __( 'Meeting Rooms', 'booking' ), |
| 606 |
'Escape Rooms' => __( 'Escape Rooms', 'booking' ), |
| 607 |
'Spas' => __( 'Spas', 'booking' ), |
| 608 |
'Salons' => __( 'Salons', 'booking' ), |
| 609 |
'Clinics' => __( 'Clinics', 'booking' ), |
| 610 |
'Massage Therapy' => __( 'Massage Therapy', 'booking' ), |
| 611 |
'Doctors' => __( 'Doctors', 'booking' ), |
| 612 |
'Dentists' => __( 'Dentists', 'booking' ), |
| 613 |
'Therapists' => __( 'Therapists', 'booking' ), |
| 614 |
'Chiropractors' => __( 'Chiropractors', 'booking' ), |
| 615 |
'Acupuncturists' => __( 'Acupuncturists', 'booking' ), |
| 616 |
'Consultants' => __( 'Consultants', 'booking' ), |
| 617 |
'Lawyers' => __( 'Lawyers', 'booking' ), |
| 618 |
'Accountants' => __( 'Accountants', 'booking' ), |
| 619 |
'Financial Advisors' => __( 'Financial Advisors', 'booking' ), |
| 620 |
'Product Demonstrations' => __( 'Product Demonstrations', 'booking' ), |
| 621 |
'Fitting Appointments' => __( 'Fitting Appointments', 'booking' ), |
| 622 |
'Equipment Rentals' => __( 'Equipment Rentals', 'booking' ), |
| 623 |
'Tutors' => __( 'Tutors', 'booking' ), |
| 624 |
'Classes' => __( 'Classes', 'booking' ), |
| 625 |
'Schools' => __( 'Schools', 'booking' ), |
| 626 |
'Workshops' => __( 'Workshops', 'booking' ), |
| 627 |
'Table Reservations' => __( 'Table Reservations', 'booking' ), |
| 628 |
'Catering' => __( 'Catering', 'booking' ), |
| 629 |
'Banquet Halls' => __( 'Banquet Halls', 'booking' ), |
| 630 |
'Photographers' => __( 'Photographers', 'booking' ), |
| 631 |
'Videographers' => __( 'Videographers', 'booking' ), |
| 632 |
'Web Developers' => __( 'Web Developers', 'booking' ), |
| 633 |
'IT Support' => __( 'IT Support', 'booking' ), |
| 634 |
'Art Studios' => __( 'Art Studios', 'booking' ), |
| 635 |
'Music Lessons' => __( 'Music Lessons', 'booking' ), |
| 636 |
'Attorneys' => __( 'Attorneys', 'booking' ), |
| 637 |
'Legal Consultants' => __( 'Legal Consultants', 'booking' ), |
| 638 |
'Daycares' => __( 'Daycares', 'booking' ), |
| 639 |
'Babysitting' => __( 'Babysitting', 'booking' ), |
| 640 |
'Nanny Services' => __( 'Nanny Services', 'booking' ), |
| 641 |
'Cleaners' => __( 'Cleaners', 'booking' ), |
| 642 |
'Repair Services' => __( 'Repair Services', 'booking' ), |
| 643 |
'Handyman' => __( 'Handyman', 'booking' ), |
| 644 |
'Gyms' => __( 'Gyms', 'booking' ), |
| 645 |
'Fitness Classes' => __( 'Fitness Classes', 'booking' ), |
| 646 |
'Yoga Studios' => __( 'Yoga Studios', 'booking' ), |
| 647 |
'Golf Lessons' => __( 'Golf Lessons', 'booking' ), |
| 648 |
'Sports Equipment Rentals' => __( 'Sports Equipment Rentals', 'booking' ), |
| 649 |
'Veterinarians' => __( 'Veterinarians', 'booking' ), |
| 650 |
'Pet Groomers' => __( 'Pet Groomers', 'booking' ), |
| 651 |
'Pet Sitting' => __( 'Pet Sitting', 'booking' ), |
| 652 |
'Coaching' => __( 'Coaching', 'booking' ), |
| 653 |
'Counseling' => __( 'Counseling', 'booking' ), |
| 654 |
'Spiritual Services' => __( 'Spiritual Services', 'booking' ), |
| 655 |
'Interior Design' => __( 'Interior Design', 'booking' ), |
| 656 |
); |
| 657 |
|
| 658 |
foreach ( $options as $option_id => $option_label ) { |
| 659 |
$options[ $option_id ] = isset( $translated_labels[ $option_label ] ) ? $translated_labels[ $option_label ] : $option_label; |
| 660 |
} |
| 661 |
|
| 662 |
return $options; |
| 663 |
} |
| 664 |
|
| 665 |
/** |
| 666 |
* Return the bundled Booking Mode terminology choices allowed in this context. |
| 667 |
* |
| 668 |
* Stable identifiers come from the existing Booking Modes setup integration. |
| 669 |
* The remaining records are presentation-only metadata for the approved |
| 670 |
* administration-navigation mockup. They never authorize or apply a mode. |
| 671 |
* |
| 672 |
* @return array<int,array<string,mixed>> Card presentation records. |
| 673 |
*/ |
| 674 |
private function get_booking_experiences() { |
| 675 |
$allowed_mode_ids = array( 'appointment', 'rental', 'classic' ); |
| 676 |
if ( function_exists( 'wpbc_booking_modes_get_setup_mode_choices' ) ) { |
| 677 |
$allowed_mode_ids = array_keys( wpbc_booking_modes_get_setup_mode_choices() ); |
| 678 |
} elseif ( function_exists( 'wpbc_booking_modes_get_allowed_mode_ids' ) ) { |
| 679 |
$allowed_mode_ids = wpbc_booking_modes_get_allowed_mode_ids(); |
| 680 |
} |
| 681 |
|
| 682 |
$experience_definitions = array( |
| 683 |
'appointment' => array( |
| 684 |
'id' => 'appointment', |
| 685 |
'title' => __( 'Appointments & services', 'booking' ), |
| 686 |
'toolbar_label' => __( 'Appointments', 'booking' ), |
| 687 |
'admin_navigation' => array( |
| 688 |
array( 'label' => __( 'Appointments', 'booking' ), 'icon' => 'wpbc_icn_event_available' ), |
| 689 |
array( 'label' => __( 'Services', 'booking' ), 'icon' => 'wpbc_icn_local_offer' ), |
| 690 |
array( 'label' => __( 'Providers', 'booking' ), 'icon' => 'wpbc_icn_groups' ), |
| 691 |
array( 'label' => __( 'Working hours', 'booking' ), 'icon' => 'wpbc_icn_access_time' ), |
| 692 |
), |
| 693 |
'dashboard_uses' => __( 'Appointments, Services, Providers and Working hours.', 'booking' ), |
| 694 |
'best_for' => __( 'Consultations, salons, classes and scheduled services.', 'booking' ), |
| 695 |
), |
| 696 |
'rental' => array( |
| 697 |
'id' => 'rental', |
| 698 |
'title' => __( 'Properties & rentals', 'booking' ), |
| 699 |
'toolbar_label' => __( 'Rentals', 'booking' ), |
| 700 |
'admin_navigation' => array( |
| 701 |
array( 'label' => __( 'Bookings', 'booking' ), 'icon' => 'wpbc_icn_event' ), |
| 702 |
array( 'label' => __( 'Properties', 'booking' ), 'icon' => 'wpbc_icn_home' ), |
| 703 |
array( 'label' => __( 'Availability', 'booking' ), 'icon' => 'wpbc_icn_date_range' ), |
| 704 |
array( 'label' => __( 'Guests', 'booking' ), 'icon' => 'wpbc_icn_groups' ), |
| 705 |
), |
| 706 |
'dashboard_uses' => __( 'Bookings, Properties, Availability and Guests.', 'booking' ), |
| 707 |
'best_for' => __( 'Rooms, equipment and holiday rentals.', 'booking' ), |
| 708 |
), |
| 709 |
'classic' => array( |
| 710 |
'id' => 'classic', |
| 711 |
'title' => __( 'Classic Booking Calendar', 'booking' ), |
| 712 |
'toolbar_label' => __( 'Classic', 'booking' ), |
| 713 |
'admin_navigation' => array( |
| 714 |
array( 'label' => __( 'Bookings', 'booking' ), 'icon' => 'wpbc_icn_event' ), |
| 715 |
array( 'label' => __( 'Booking resources', 'booking' ), 'icon' => 'wpbc_icn_dashboard' ), |
| 716 |
array( 'label' => __( 'Availability', 'booking' ), 'icon' => 'wpbc_icn_date_range' ), |
| 717 |
array( 'label' => __( 'Booking forms', 'booking' ), 'icon' => 'wpbc_icn_description' ), |
| 718 |
), |
| 719 |
'dashboard_uses' => __( 'Bookings, Booking resources, Availability and Booking forms.', 'booking' ), |
| 720 |
'best_for' => __( 'Traditional resource-first booking.', 'booking' ), |
| 721 |
), |
| 722 |
); |
| 723 |
|
| 724 |
$experiences = array(); |
| 725 |
foreach ( array( 'appointment', 'rental', 'classic' ) as $mode_id ) { |
| 726 |
if ( in_array( $mode_id, $allowed_mode_ids, true ) ) { |
| 727 |
$experience = $experience_definitions[ $mode_id ]; |
| 728 |
$mode = function_exists( 'wpbc_booking_modes_get_mode' ) ? wpbc_booking_modes_get_mode( $mode_id ) : null; |
| 729 |
|
| 730 |
if ( is_array( $mode ) && ! empty( $mode['label'] ) && is_scalar( $mode['label'] ) ) { |
| 731 |
$experience['toolbar_label'] = sanitize_text_field( (string) $mode['label'] ); |
| 732 |
} |
| 733 |
|
| 734 |
/* translators: %s: Booking Calendar administration mode. */ |
| 735 |
$experience['toolbar_title'] = sprintf( __( 'Mode: %s', 'booking' ), $experience['toolbar_label'] ); |
| 736 |
$experiences[] = $experience; |
| 737 |
} |
| 738 |
} |
| 739 |
|
| 740 |
return $experiences; |
| 741 |
} |
| 742 |
|
| 743 |
/** |
| 744 |
* Resolve the Page 4 Booking Mode proposal used to organize Page 5. |
| 745 |
* |
| 746 |
* The current owner-scoped Booking Mode always wins over an older checkpoint |
| 747 |
* snapshot. This method never writes the canonical `booking_admin_mode` user |
| 748 |
* option. |
| 749 |
* |
| 750 |
* @param array<string,mixed> $draft_values Normalized draft values by step. |
| 751 |
* |
| 752 |
* @return string One allowed bundled Booking Mode identifier. |
| 753 |
*/ |
| 754 |
private function get_selected_booking_mode_id( array $draft_values ) { |
| 755 |
$experience_ids = array_column( $this->get_booking_experiences(), 'id' ); |
| 756 |
$experience_values = $this->get_step_values( 'booking_experience', $draft_values ); |
| 757 |
$selected_mode_id = isset( $experience_values['booking_experience'] ) |
| 758 |
? sanitize_key( (string) $experience_values['booking_experience'] ) |
| 759 |
: ''; |
| 760 |
|
| 761 |
if ( in_array( $selected_mode_id, $experience_ids, true ) ) { |
| 762 |
return $selected_mode_id; |
| 763 |
} |
| 764 |
|
| 765 |
return ! empty( $experience_ids ) ? (string) reset( $experience_ids ) : 'classic'; |
| 766 |
} |
| 767 |
|
| 768 |
/** |
| 769 |
* Build the Page 5 recommended and additional groups for one Booking Mode. |
| 770 |
* |
| 771 |
* All registered journeys remain visible. Branch-compatible journeys are |
| 772 |
* ordered first, while the remaining choices are presented as visually muted |
| 773 |
* additional options. Edition-owned availability is attached by the master |
| 774 |
* registry and independently enforced by the field allow-list, so presentation |
| 775 |
* state never becomes an authorization boundary. |
| 776 |
* |
| 777 |
* @param string $mode_id Validated Page 4 Booking Mode identifier. |
| 778 |
* |
| 779 |
* @return array<int,array<string,mixed>> Ordered presentation groups. |
| 780 |
*/ |
| 781 |
public function get_customer_journey_groups( $mode_id ) { |
| 782 |
$mode_id = sanitize_key( (string) $mode_id ); |
| 783 |
$journeys = self::get_customer_journeys(); |
| 784 |
$recommended_order = array( |
| 785 |
'appointment' => array( 'guided_appointment_flow', 'start_end_time', 'start_time_duration', 'fixed_time_slots' ), |
| 786 |
'rental' => array( 'single_full_day', 'multiple_independent_days', 'flexible_date_range', 'fixed_length_date_range', 'check_in_out_changeover' ), |
| 787 |
'classic' => array( 'single_full_day', 'multiple_independent_days', 'flexible_date_range', 'fixed_length_date_range', 'check_in_out_changeover', 'fixed_time_slots', 'start_end_time', 'start_time_duration', 'repeated_time_multiple_dates', 'first_start_last_end' ), |
| 788 |
); |
| 789 |
|
| 790 |
if ( ! isset( $recommended_order[ $mode_id ] ) ) { |
| 791 |
$mode_id = 'classic'; |
| 792 |
} |
| 793 |
|
| 794 |
$recommended_journeys = array(); |
| 795 |
foreach ( $recommended_order[ $mode_id ] as $journey_id ) { |
| 796 |
if ( isset( $journeys[ $journey_id ] ) && in_array( $mode_id, $journeys[ $journey_id ]['branches'], true ) ) { |
| 797 |
$recommended_journeys[ $journey_id ] = $journeys[ $journey_id ]; |
| 798 |
} |
| 799 |
} |
| 800 |
|
| 801 |
foreach ( $journeys as $journey_id => $journey ) { |
| 802 |
if ( in_array( $mode_id, $journey['branches'], true ) && ! isset( $recommended_journeys[ $journey_id ] ) ) { |
| 803 |
$recommended_journeys[ $journey_id ] = $journey; |
| 804 |
} |
| 805 |
} |
| 806 |
|
| 807 |
$additional_journeys = array_diff_key( $journeys, $recommended_journeys ); |
| 808 |
$mode_titles = array( |
| 809 |
'appointment' => __( 'Appointments & services', 'booking' ), |
| 810 |
'rental' => __( 'Properties & rentals', 'booking' ), |
| 811 |
'classic' => __( 'Classic Booking Calendar', 'booking' ), |
| 812 |
); |
| 813 |
|
| 814 |
$groups = array( |
| 815 |
array( |
| 816 |
'id' => 'recommended', |
| 817 |
/* translators: %s: Selected Booking Mode title. */ |
| 818 |
'label' => sprintf( __( 'Recommended for %s', 'booking' ), $mode_titles[ $mode_id ] ), |
| 819 |
'description' => __( 'These customer journeys best match the admin terminology selected in the previous step.', 'booking' ), |
| 820 |
'is_additional' => false, |
| 821 |
'journeys' => $recommended_journeys, |
| 822 |
), |
| 823 |
); |
| 824 |
|
| 825 |
if ( ! empty( $additional_journeys ) ) { |
| 826 |
$groups[] = array( |
| 827 |
'id' => 'additional', |
| 828 |
'label' => __( 'Additional customer journeys', 'booking' ), |
| 829 |
'description' => __( 'These alternatives remain available when your customer flow differs from the selected admin terminology.', 'booking' ), |
| 830 |
'is_additional' => true, |
| 831 |
'journeys' => $additional_journeys, |
| 832 |
); |
| 833 |
} |
| 834 |
|
| 835 |
return $groups; |
| 836 |
} |
| 837 |
|
| 838 |
/** |
| 839 |
* Return the complete Phase 3 customer-journey registry. |
| 840 |
* |
| 841 |
* The registry intentionally returns every unique source-backed journey. |
| 842 |
* The `branches` metadata lets Page 5 recommend and order journeys for the |
| 843 |
* Page 4 Booking Mode without removing alternatives. Stable identifiers are |
| 844 |
* the draft contract; labels, descriptions, previews, and branch metadata |
| 845 |
* are presentation information. |
| 846 |
* |
| 847 |
* @return array<string,array<string,mixed>> Journey records keyed by stable identifier. |
| 848 |
*/ |
| 849 |
public static function get_customer_journeys() { |
| 850 |
$journeys = array( |
| 851 |
'guided_appointment_flow' => array( |
| 852 |
'id' => 'guided_appointment_flow', |
| 853 |
'title' => __( 'Guided appointment flow', 'booking' ), |
| 854 |
'description' => __( 'Customers choose a Service, Provider and available start time step by step.', 'booking' ), |
| 855 |
'journey' => array( __( 'Service', 'booking' ), __( 'Provider', 'booking' ), __( 'Date', 'booking' ), __( 'Time', 'booking' ), __( 'Details', 'booking' ) ), |
| 856 |
'preview_types' => array( 'service', 'providers', 'calendar', 'times', 'details' ), |
| 857 |
'best_for' => __( 'Businesses with several Services or Providers.', 'booking' ), |
| 858 |
'branches' => array( 'appointment' ), |
| 859 |
), |
| 860 |
'start_end_time' => array( |
| 861 |
'id' => 'start_end_time', |
| 862 |
'title' => __( 'Start & end time', 'booking' ), |
| 863 |
'description' => __( 'Customers choose both ends of a flexible appointment time range.', 'booking' ), |
| 864 |
'journey' => array( __( 'Date', 'booking' ), __( 'Start time', 'booking' ), __( 'End time', 'booking' ), __( 'Details', 'booking' ) ), |
| 865 |
'preview_types' => array( 'calendar', 'times', 'times', 'details' ), |
| 866 |
'best_for' => __( 'Flexible appointments, equipment use and variable-duration work.', 'booking' ), |
| 867 |
'branches' => array( 'appointment', 'classic' ), |
| 868 |
), |
| 869 |
'start_time_duration' => array( |
| 870 |
'id' => 'start_time_duration', |
| 871 |
'title' => __( 'Start time + duration', 'booking' ), |
| 872 |
'description' => __( 'Customers choose a start time and one of the available durations.', 'booking' ), |
| 873 |
'journey' => array( __( 'Date', 'booking' ), __( 'Start time', 'booking' ), __( 'Duration', 'booking' ), __( 'Details', 'booking' ) ), |
| 874 |
'preview_types' => array( 'calendar', 'times', 'times', 'details' ), |
| 875 |
'best_for' => __( 'Appointments offered in standard duration choices.', 'booking' ), |
| 876 |
'branches' => array( 'appointment', 'classic' ), |
| 877 |
), |
| 878 |
'fixed_time_slots' => array( |
| 879 |
'id' => 'fixed_time_slots', |
| 880 |
'title' => __( 'Fixed time slots', 'booking' ), |
| 881 |
'description' => __( 'Customers select one predefined available time slot.', 'booking' ), |
| 882 |
'journey' => array( __( 'Date', 'booking' ), __( 'Available slot', 'booking' ), __( 'Details', 'booking' ) ), |
| 883 |
'preview_types' => array( 'calendar', 'times', 'details' ), |
| 884 |
'best_for' => __( 'Classes, consultations and fixed timetables.', 'booking' ), |
| 885 |
'branches' => array( 'appointment', 'classic' ), |
| 886 |
), |
| 887 |
'single_full_day' => array( |
| 888 |
'id' => 'single_full_day', |
| 889 |
'title' => __( 'Single full day', 'booking' ), |
| 890 |
'description' => __( 'Customers reserve one complete date without choosing a time.', 'booking' ), |
| 891 |
'journey' => array( __( 'Date', 'booking' ), __( 'Details', 'booking' ) ), |
| 892 |
'preview_types' => array( 'calendar', 'details' ), |
| 893 |
'best_for' => __( 'One-day rentals, events and daily reservations.', 'booking' ), |
| 894 |
'branches' => array( 'rental', 'classic' ), |
| 895 |
), |
| 896 |
'multiple_independent_days' => array( |
| 897 |
'id' => 'multiple_independent_days', |
| 898 |
'title' => __( 'Multiple independent full days', 'booking' ), |
| 899 |
'description' => __( 'Customers select several separate full dates in one booking.', 'booking' ), |
| 900 |
'journey' => array( __( 'Dates', 'booking' ), __( 'Details', 'booking' ) ), |
| 901 |
'preview_types' => array( 'calendar-multiple', 'details' ), |
| 902 |
'best_for' => __( 'Bookings that cover several non-consecutive days.', 'booking' ), |
| 903 |
'branches' => array( 'rental', 'classic' ), |
| 904 |
), |
| 905 |
'flexible_date_range' => array( |
| 906 |
'id' => 'flexible_date_range', |
| 907 |
'title' => __( 'Flexible date range', 'booking' ), |
| 908 |
'description' => __( 'Customers choose the first and last date of a variable-length booking.', 'booking' ), |
| 909 |
'journey' => array( __( 'Start date', 'booking' ), __( 'End date', 'booking' ), __( 'Details', 'booking' ) ), |
| 910 |
'preview_types' => array( 'range-flexible-start', 'range-flexible-end', 'details' ), |
| 911 |
'best_for' => __( 'Stays and rentals whose length varies by booking.', 'booking' ), |
| 912 |
'branches' => array( 'rental', 'classic' ), |
| 913 |
), |
| 914 |
'fixed_length_date_range' => array( |
| 915 |
'id' => 'fixed_length_date_range', |
| 916 |
'title' => __( 'Fixed-length date range', 'booking' ), |
| 917 |
'description' => __( 'Customers choose a start date and the configured range is selected automatically.', 'booking' ), |
| 918 |
'journey' => array( __( 'Start date', 'booking' ), __( 'Fixed stay', 'booking' ), __( 'Details', 'booking' ) ), |
| 919 |
'preview_types' => array( 'range-fixed-start', 'range-fixed-selected', 'details' ), |
| 920 |
'best_for' => __( 'Weekend, multi-day and weekly packages.', 'booking' ), |
| 921 |
'branches' => array( 'rental', 'classic' ), |
| 922 |
), |
| 923 |
'check_in_out_changeover' => array( |
| 924 |
'id' => 'check_in_out_changeover', |
| 925 |
'title' => __( 'Check-in & check-out changeover', 'booking' ), |
| 926 |
'description' => __( 'Customers choose arrival and departure dates with changeover boundaries.', 'booking' ), |
| 927 |
'journey' => array( __( 'Check-in', 'booking' ), __( 'Check-out', 'booking' ), __( 'Guest details', 'booking' ) ), |
| 928 |
'preview_types' => array( 'range-check-in', 'range-check-out', 'guest-details' ), |
| 929 |
'best_for' => __( 'Accommodation and rentals with arrival and departure times.', 'booking' ), |
| 930 |
'branches' => array( 'rental', 'classic' ), |
| 931 |
), |
| 932 |
'repeated_time_multiple_dates' => array( |
| 933 |
'id' => 'repeated_time_multiple_dates', |
| 934 |
'title' => __( 'Repeat one time on multiple dates', 'booking' ), |
| 935 |
'description' => __( 'Customers select several dates and use the same time on every selected date.', 'booking' ), |
| 936 |
'journey' => array( __( 'Dates', 'booking' ), __( 'Time', 'booking' ), __( 'Details', 'booking' ) ), |
| 937 |
'preview_types' => array( 'calendar-multiple', 'times', 'details' ), |
| 938 |
'best_for' => __( 'Recurring sessions submitted together for the same daily time.', 'booking' ), |
| 939 |
'branches' => array( 'classic' ), |
| 940 |
), |
| 941 |
'first_start_last_end' => array( |
| 942 |
'id' => 'first_start_last_end', |
| 943 |
'title' => __( 'First-date start & last-date end', 'booking' ), |
| 944 |
'description' => __( 'Customers choose a start on the first date and an end on the last date.', 'booking' ), |
| 945 |
'journey' => array( __( 'Start date & time', 'booking' ), __( 'End date & time', 'booking' ), __( 'Details', 'booking' ) ), |
| 946 |
'preview_types' => array( 'date-time-start', 'date-time-end', 'details' ), |
| 947 |
'best_for' => __( 'Multi-day bookings with different arrival and departure times.', 'booking' ), |
| 948 |
'branches' => array( 'classic' ), |
| 949 |
), |
| 950 |
); |
| 951 |
|
| 952 |
foreach ( $journeys as $journey_id => $journey ) { |
| 953 |
$journeys[ $journey_id ]['edition_requirement'] = WPBC_Setup_Wizard_Customer_Journey_Policy::get_journey_edition_requirement( $journey_id ); |
| 954 |
} |
| 955 |
|
| 956 |
return $journeys; |
| 957 |
} |
| 958 |
|
| 959 |
} |
| 960 |
|