| 1 |
<?php |
| 2 |
/** Appointment Services presentation helpers. @package Booking Calendar */ |
| 3 |
if ( ! defined( 'ABSPATH' ) ) { |
| 4 |
exit; |
| 5 |
} |
| 6 |
|
| 7 |
/** |
| 8 |
* Build capability-aware semantic administration links for Providers. |
| 9 |
* |
| 10 |
* Providers remain Booking Resources. These routes expose familiar |
| 11 |
* Appointment terminology while preserving the existing availability and |
| 12 |
* Service assignment controllers as the single source of truth. |
| 13 |
* |
| 14 |
* @param int $resource_id Optional Provider booking resource ID. |
| 15 |
* @param bool $include_service_assignments Whether to include the Services-catalog shortcut. |
| 16 |
* |
| 17 |
* @return array<int,array<string,string>> Link title, URL, and icon values. |
| 18 |
*/ |
| 19 |
function wpbc_appointment_services_get_provider_admin_links( $resource_id = 0, $include_service_assignments = true ) { |
| 20 |
$resource_id = absint( $resource_id ); |
| 21 |
$links = array(); |
| 22 |
|
| 23 |
if ( wpbc_appointment_services_can_manage_availability() ) { |
| 24 |
$working_hours_url = function_exists( 'wpbc_get_general_availability_url' ) |
| 25 |
? wpbc_get_general_availability_url() |
| 26 |
: admin_url( 'admin.php?page=wpbc-availability&tab=general_availability' ); |
| 27 |
$days_off_url = function_exists( 'wpbc_get_availability_url' ) |
| 28 |
? wpbc_get_availability_url() |
| 29 |
: admin_url( 'admin.php?page=wpbc-availability' ); |
| 30 |
$working_hours_url = add_query_arg( 'wpbc_ag_open', 'working_time', $working_hours_url ); |
| 31 |
if ( $resource_id ) { |
| 32 |
$working_hours_url = add_query_arg( 'resource_id', $resource_id, $working_hours_url ); |
| 33 |
$days_off_url = add_query_arg( 'resource_id', $resource_id, $days_off_url ); |
| 34 |
} |
| 35 |
$links[] = array( |
| 36 |
'title' => __( 'Working Hours', 'booking' ), |
| 37 |
'url' => $working_hours_url, |
| 38 |
'icon' => 'wpbc-bi-clock', |
| 39 |
); |
| 40 |
$links[] = array( |
| 41 |
'title' => __( 'Days Off', 'booking' ), |
| 42 |
'url' => $days_off_url, |
| 43 |
'icon' => 'wpbc-bi-calendar-x', |
| 44 |
); |
| 45 |
} |
| 46 |
|
| 47 |
if ( $include_service_assignments && current_user_can( wpbc_appointment_services_get_manage_capability() ) ) { |
| 48 |
$service_assignments_url = admin_url( 'admin.php?page=wpbc-services' ); |
| 49 |
$services_link_title = __( 'Manage Services', 'booking' ); |
| 50 |
if ( $resource_id ) { |
| 51 |
$service_assignments_url = add_query_arg( 'provider_id', $resource_id, $service_assignments_url ); |
| 52 |
$services_link_title = __( 'Assigned Services', 'booking' ); |
| 53 |
} |
| 54 |
$links[] = array( |
| 55 |
'title' => $services_link_title, |
| 56 |
'url' => $service_assignments_url, |
| 57 |
'icon' => 'wpbc-bi-grid', |
| 58 |
); |
| 59 |
} |
| 60 |
|
| 61 |
return (array) apply_filters( 'wpbc_appointment_provider_admin_links', $links, $resource_id, $include_service_assignments ); |
| 62 |
} |
| 63 |
|
| 64 |
/** |
| 65 |
* Render semantic Provider administration links using standard WP buttons. |
| 66 |
* |
| 67 |
* @param int $resource_id Optional Provider booking resource ID. |
| 68 |
* @param string $css_class Optional additional wrapper class. |
| 69 |
* @param bool $include_service_assignments Whether to include the Services-catalog shortcut. |
| 70 |
* |
| 71 |
* @return void |
| 72 |
*/ |
| 73 |
function wpbc_appointment_services_render_provider_admin_links( $resource_id = 0, $css_class = '', $include_service_assignments = true ) { |
| 74 |
$links = wpbc_appointment_services_get_provider_admin_links( $resource_id, $include_service_assignments ); |
| 75 |
if ( empty( $links ) ) { |
| 76 |
return; |
| 77 |
} |
| 78 |
?> |
| 79 |
<div class="wpbc_appointment_provider_links wpbc_ui_el__buttons_group <?php echo esc_attr( sanitize_html_class( $css_class ) ); ?>"> |
| 80 |
<?php foreach ( $links as $link ) : ?> |
| 81 |
<a class="button button-secondary" href="<?php echo esc_url( $link['url'] ); ?>"> |
| 82 |
<i class="menu_icon icon-1x <?php echo esc_attr( $link['icon'] ); ?>" aria-hidden="true"></i> |
| 83 |
<?php echo esc_html( $link['title'] ); ?> |
| 84 |
</a> |
| 85 |
<?php endforeach; ?> |
| 86 |
</div> |
| 87 |
<?php |
| 88 |
} |
| 89 |
|
| 90 |
/** |
| 91 |
* Add a mode-aware setup guide above the existing Resources table. |
| 92 |
* |
| 93 |
* Appointment mode retains Provider and Service terminology. Rental and |
| 94 |
* Classic modes use their native resource terminology and omit the |
| 95 |
* Appointment-only Services action. |
| 96 |
* |
| 97 |
* @param string $settings_section Existing Resources page section identifier. |
| 98 |
* |
| 99 |
* @return void |
| 100 |
*/ |
| 101 |
function wpbc_appointment_services_render_provider_tools( $settings_section ) { |
| 102 |
if ( |
| 103 |
'resources' !== $settings_section |
| 104 |
|| ! function_exists( 'wpbc_is_resources_page' ) |
| 105 |
|| ! wpbc_is_resources_page() |
| 106 |
) { |
| 107 |
return; |
| 108 |
} |
| 109 |
|
| 110 |
$selected_mode_id = function_exists( 'wpbc_booking_modes_get_selected_mode_id' ) |
| 111 |
? wpbc_booking_modes_get_selected_mode_id() |
| 112 |
: 'classic'; |
| 113 |
$guide_title = __( 'Booking Resources and availability', 'booking' ); |
| 114 |
$guide_description = __( 'Configure available weekdays and dates for each Booking Resource, then use the Publish buttons below to add its booking form to a page.', 'booking' ); |
| 115 |
$guide_links = array(); |
| 116 |
$guide_html_id = 'wpbc_resources_booking_resources_availability_guide'; |
| 117 |
|
| 118 |
if ( 'appointment' === $selected_mode_id ) { |
| 119 |
$guide_title = __( 'Provider schedules and Services', 'booking' ); |
| 120 |
$guide_description = __( 'Providers use Booking Resources to manage availability. Configure their working hours and days off, then assign them to Services.', 'booking' ); |
| 121 |
$guide_links = wpbc_appointment_services_get_provider_admin_links( 0, true ); |
| 122 |
$guide_html_id = 'wpbc_resources_provider_services_guide'; |
| 123 |
} else { |
| 124 |
if ( 'rental' === $selected_mode_id ) { |
| 125 |
$guide_title = __( 'Property availability and publishing', 'booking' ); |
| 126 |
$guide_description = __( 'Properties use Booking Resources to manage availability. Configure available weekdays and dates, then publish each property\'s booking form below.', 'booking' ); |
| 127 |
$guide_html_id = 'wpbc_resources_property_availability_publishing_guide'; |
| 128 |
} |
| 129 |
|
| 130 |
if ( wpbc_appointment_services_can_manage_availability() ) { |
| 131 |
$weekdays_availability_url = function_exists( 'wpbc_get_general_availability_url' ) |
| 132 |
? wpbc_get_general_availability_url() |
| 133 |
: admin_url( 'admin.php?page=wpbc-availability&tab=general_availability' ); |
| 134 |
$days_availability_url = function_exists( 'wpbc_get_availability_url' ) |
| 135 |
? wpbc_get_availability_url() |
| 136 |
: admin_url( 'admin.php?page=wpbc-availability' ); |
| 137 |
|
| 138 |
$guide_links[] = array( |
| 139 |
'title' => ( 'rental' === $selected_mode_id ) ? __( 'Weekdays Availability', 'booking' ) : __( 'General Availability', 'booking' ), |
| 140 |
'url' => $weekdays_availability_url, |
| 141 |
'icon' => 'wpbc-bi-calendar-week', |
| 142 |
); |
| 143 |
$guide_links[] = array( |
| 144 |
'title' => __( 'Days Availability', 'booking' ), |
| 145 |
'url' => $days_availability_url, |
| 146 |
'icon' => 'wpbc-bi-calendar-check', |
| 147 |
); |
| 148 |
} |
| 149 |
} |
| 150 |
|
| 151 |
if ( function_exists( 'wpbc_is_dismissed_panel_visible' ) && ! wpbc_is_dismissed_panel_visible( $guide_html_id ) ) { |
| 152 |
return; |
| 153 |
} |
| 154 |
|
| 155 |
?> |
| 156 |
<div id="<?php echo esc_attr( $guide_html_id ); ?>" class="wpbc_appointment_provider_tools is-dismissible"> |
| 157 |
<?php |
| 158 |
if ( function_exists( 'wpbc_is_dismissed' ) ) { |
| 159 |
wpbc_is_dismissed( |
| 160 |
$guide_html_id, |
| 161 |
array( |
| 162 |
'title' => '<span class="wpbc-bi-x-lg" aria-hidden="true"></span><span class="screen-reader-text">' . esc_html__( 'Dismiss', 'booking' ) . '</span>', |
| 163 |
'hint' => __( 'Dismiss', 'booking' ), |
| 164 |
'class' => 'wpbc_appointment_provider_tools__dismiss', |
| 165 |
'is_apply_in_demo' => true, |
| 166 |
) |
| 167 |
); |
| 168 |
} |
| 169 |
?> |
| 170 |
<div class="wpbc_appointment_provider_tools__copy"> |
| 171 |
<strong><?php echo esc_html( $guide_title ); ?></strong> |
| 172 |
<span><?php echo esc_html( $guide_description ); ?></span> |
| 173 |
</div> |
| 174 |
<?php if ( ! empty( $guide_links ) ) : ?> |
| 175 |
<div class="wpbc_appointment_provider_links wpbc_ui_el__buttons_group wpbc_appointment_provider_tools__actions"> |
| 176 |
<?php foreach ( $guide_links as $guide_link ) : ?> |
| 177 |
<a class="button button-secondary" href="<?php echo esc_url( $guide_link['url'] ); ?>"> |
| 178 |
<i class="menu_icon icon-1x <?php echo esc_attr( $guide_link['icon'] ); ?>" aria-hidden="true"></i> |
| 179 |
<?php echo esc_html( $guide_link['title'] ); ?> |
| 180 |
</a> |
| 181 |
<?php endforeach; ?> |
| 182 |
</div> |
| 183 |
<?php endif; ?> |
| 184 |
</div> |
| 185 |
<?php |
| 186 |
} |
| 187 |
add_action( 'wpbc_hook_settings_page_before_content_table', 'wpbc_appointment_services_render_provider_tools', 15, 1 ); |
| 188 |
|
| 189 |
/** |
| 190 |
* Render one disabled Service inspector field for client-side editing. |
| 191 |
* |
| 192 |
* @param string $field_id Stable field and DOM identifier. |
| 193 |
* @param string $label User-facing field label. |
| 194 |
* @param string $type Input type, textarea, or status. |
| 195 |
* @param array<string,mixed> $args Optional min, step, placeholder, help, and slider values. |
| 196 |
* |
| 197 |
* @return void |
| 198 |
*/ |
| 199 |
function wpbc_appointment_services_render_field( $field_id, $label, $type = 'text', $args = array() ) { |
| 200 |
$args = wp_parse_args( |
| 201 |
$args, |
| 202 |
array( |
| 203 |
'min' => '', |
| 204 |
'step' => '', |
| 205 |
'placeholder' => '', |
| 206 |
'help' => '', |
| 207 |
'slider' => false, |
| 208 |
'slider_min' => 0, |
| 209 |
'slider_max' => 100, |
| 210 |
'slider_step' => 1, |
| 211 |
) |
| 212 |
); |
| 213 |
$slider_aria_label = ''; |
| 214 |
if ( $args['slider'] ) { |
| 215 |
/* translators: %s: Service numeric field label. */ |
| 216 |
$slider_aria_label = sprintf( __( 'Adjust %s', 'booking' ), $label ); |
| 217 |
} |
| 218 |
?> |
| 219 |
<div class="inspector__row"> |
| 220 |
<?php if ( 'status' === $type ) : ?> |
| 221 |
<span id="<?php echo esc_attr( $field_id ); ?>_label" class="inspector__label"><?php echo esc_html( $label ); ?></span> |
| 222 |
<?php else : ?> |
| 223 |
<label for="<?php echo esc_attr( $field_id ); ?>" class="inspector__label"><?php echo esc_html( $label ); ?></label> |
| 224 |
<?php endif; ?> |
| 225 |
<div class="inspector__control"> |
| 226 |
<?php if ( 'textarea' === $type ) : ?> |
| 227 |
<textarea id="<?php echo esc_attr( $field_id ); ?>" class="inspector__input" data-service-field="<?php echo esc_attr( $field_id ); ?>" rows="4" disabled></textarea> |
| 228 |
<?php elseif ( 'status' === $type ) : ?> |
| 229 |
<input type="hidden" id="<?php echo esc_attr( $field_id ); ?>" data-service-field="<?php echo esc_attr( $field_id ); ?>" value="active" disabled /> |
| 230 |
<div class="wpbc_appointment_services__status_options" role="radiogroup" aria-labelledby="<?php echo esc_attr( $field_id ); ?>_label"> |
| 231 |
<label for="<?php echo esc_attr( $field_id ); ?>_active"> |
| 232 |
<input type="radio" id="<?php echo esc_attr( $field_id ); ?>_active" name="wpbc_appointment_service_status" value="active" data-service-status-choice checked disabled /> |
| 233 |
<span><?php esc_html_e( 'Active', 'booking' ); ?></span> |
| 234 |
</label> |
| 235 |
<label for="<?php echo esc_attr( $field_id ); ?>_inactive"> |
| 236 |
<input type="radio" id="<?php echo esc_attr( $field_id ); ?>_inactive" name="wpbc_appointment_service_status" value="inactive" data-service-status-choice disabled /> |
| 237 |
<span><?php esc_html_e( 'Inactive', 'booking' ); ?></span> |
| 238 |
</label> |
| 239 |
<label for="<?php echo esc_attr( $field_id ); ?>_archived"> |
| 240 |
<input type="radio" id="<?php echo esc_attr( $field_id ); ?>_archived" name="wpbc_appointment_service_status" value="archived" data-service-status-choice disabled /> |
| 241 |
<span><?php esc_html_e( 'Archived', 'booking' ); ?></span> |
| 242 |
</label> |
| 243 |
</div> |
| 244 |
<?php else : ?> |
| 245 |
<div class="<?php echo $args['slider'] ? 'wpbc_appointment_services__number_control' : ''; ?>"> |
| 246 |
<input type="<?php echo esc_attr( $type ); ?>" id="<?php echo esc_attr( $field_id ); ?>" class="inspector__input" data-service-field="<?php echo esc_attr( $field_id ); ?>" <?php echo '' !== $args['min'] ? 'min="' . esc_attr( $args['min'] ) . '"' : ''; ?> <?php echo '' !== $args['step'] ? 'step="' . esc_attr( $args['step'] ) . '"' : ''; ?> placeholder="<?php echo esc_attr( $args['placeholder'] ); ?>" disabled /> |
| 247 |
<?php if ( $args['slider'] ) : ?> |
| 248 |
<input type="range" |
| 249 |
class="wpbc_appointment_services__number_slider" |
| 250 |
data-service-range-field="<?php echo esc_attr( $field_id ); ?>" |
| 251 |
data-service-range-default-min="<?php echo esc_attr( $args['slider_min'] ); ?>" |
| 252 |
data-service-range-default-max="<?php echo esc_attr( $args['slider_max'] ); ?>" |
| 253 |
min="<?php echo esc_attr( $args['slider_min'] ); ?>" |
| 254 |
max="<?php echo esc_attr( $args['slider_max'] ); ?>" |
| 255 |
step="<?php echo esc_attr( $args['slider_step'] ); ?>" |
| 256 |
value="<?php echo esc_attr( $args['slider_min'] ); ?>" |
| 257 |
aria-label="<?php echo esc_attr( $slider_aria_label ); ?>" |
| 258 |
disabled /> |
| 259 |
<?php endif; ?> |
| 260 |
</div> |
| 261 |
<?php endif; ?> |
| 262 |
<?php if ( $args['help'] ) : ?> |
| 263 |
<p class="description wpbc_bfb__help"><?php echo esc_html( $args['help'] ); ?></p> |
| 264 |
<?php endif; ?> |
| 265 |
</div> |
| 266 |
</div> |
| 267 |
<?php |
| 268 |
} |
| 269 |
|
| 270 |
/** |
| 271 |
* Render the Service image selector using the shared WordPress media library. |
| 272 |
* |
| 273 |
* The URL is stored with the Service payload while the media attachment remains |
| 274 |
* managed by WordPress. A readonly URL field keeps the AJAX editor contract |
| 275 |
* transparent and allows the common media-uploader helper to populate it. |
| 276 |
* |
| 277 |
* @return void |
| 278 |
*/ |
| 279 |
function wpbc_appointment_services_render_picture_field() { |
| 280 |
?> |
| 281 |
<div class="inspector__row wpbc_ui_listing__picture_row wpbc_appointment_services__picture_row"> |
| 282 |
<label for="picture_url" class="inspector__label"><?php esc_html_e( 'Picture', 'booking' ); ?></label> |
| 283 |
<div class="inspector__control"> |
| 284 |
<div class="wpbc_ui_listing__media wpbc_appointment_services__media"> |
| 285 |
<button type="button" class="wpbc_ui_listing__media_preview wpbc_appointment_services__media_preview wpbc_media_upload_button" data-modal_title="<?php esc_attr_e( 'Select Service Image', 'booking' ); ?>" data-btn_title="<?php esc_attr_e( 'Use this image', 'booking' ); ?>" data-url_field="picture_url" aria-label="<?php esc_attr_e( 'Select Service image', 'booking' ); ?>" disabled> |
| 286 |
<img alt="" class="wpbc_ui_listing__media_image wpbc_appointment_services__media_image" hidden /> |
| 287 |
<i class="wpbc_ui_listing__media_placeholder wpbc_appointment_services__media_placeholder menu_icon icon-1x wpbc-bi-image-fill" aria-hidden="true"></i> |
| 288 |
</button> |
| 289 |
<div class="wpbc_ui_listing__media_actions wpbc_appointment_services__media_actions wpbc_ui_el__buttons_group"> |
| 290 |
<button type="button" class="button wpbc_media_upload_button wpbc_appointment_services__select_image" data-modal_title="<?php esc_attr_e( 'Select Service Image', 'booking' ); ?>" data-btn_title="<?php esc_attr_e( 'Use this image', 'booking' ); ?>" data-url_field="picture_url" disabled><?php esc_html_e( 'Select image', 'booking' ); ?></button> |
| 291 |
<button type="button" class="button wpbc_appointment_services__remove_image" disabled><?php esc_html_e( 'Remove', 'booking' ); ?></button> |
| 292 |
</div> |
| 293 |
</div> |
| 294 |
<input type="text" id="picture_url" class="inspector__input" data-service-field="picture_url" value="" placeholder="<?php esc_attr_e( 'Select image...', 'booking' ); ?>" readonly disabled /> |
| 295 |
<p class="description wpbc_bfb__help"><?php esc_html_e( 'Used in the Services catalog and Appointment selection.', 'booking' ); ?></p> |
| 296 |
</div> |
| 297 |
</div> |
| 298 |
<?php |
| 299 |
} |
| 300 |
|
| 301 |
/** |
| 302 |
* Render the general Service fields for the inspector callback. |
| 303 |
* |
| 304 |
* @return void |
| 305 |
*/ |
| 306 |
function wpbc_appointment_services_render_general_fields() { |
| 307 |
wpbc_appointment_services_render_field( 'title', __( 'Service title', 'booking' ), 'text', array( 'placeholder' => __( 'Example: Initial Consultation', 'booking' ) ) ); |
| 308 |
wpbc_appointment_services_render_field( 'description', __( 'Description', 'booking' ), 'textarea' ); |
| 309 |
wpbc_appointment_services_render_picture_field(); |
| 310 |
wpbc_appointment_services_render_field( 'status', __( 'Status', 'booking' ), 'status' ); |
| 311 |
} |
| 312 |
|
| 313 |
/** |
| 314 |
* Render duration and buffer fields for the inspector. |
| 315 |
* |
| 316 |
* @return void |
| 317 |
*/ |
| 318 |
function wpbc_appointment_services_render_scheduling_fields() { |
| 319 |
|
| 320 |
wpbc_appointment_services_render_field( |
| 321 |
'duration_minutes', |
| 322 |
__( 'Duration (minutes)', 'booking' ), |
| 323 |
'number', |
| 324 |
array( |
| 325 |
'min' => 1, |
| 326 |
'step' => 1, |
| 327 |
'slider' => true, |
| 328 |
'slider_min' => 1, |
| 329 |
'slider_max' => 480, |
| 330 |
'slider_step' => 5, |
| 331 |
) |
| 332 |
); |
| 333 |
wpbc_appointment_services_render_field( |
| 334 |
'buffer_before_minutes', |
| 335 |
__( 'Buffer before (minutes)', 'booking' ), |
| 336 |
'number', |
| 337 |
array( |
| 338 |
'min' => 0, |
| 339 |
'step' => 1, |
| 340 |
'slider' => true, |
| 341 |
'slider_min' => 0, |
| 342 |
'slider_max' => 180, |
| 343 |
'slider_step' => 5, |
| 344 |
) |
| 345 |
); |
| 346 |
wpbc_appointment_services_render_field( |
| 347 |
'buffer_after_minutes', |
| 348 |
__( 'Buffer after (minutes)', 'booking' ), |
| 349 |
'number', |
| 350 |
array( |
| 351 |
'min' => 0, |
| 352 |
'step' => 1, |
| 353 |
'slider' => true, |
| 354 |
'slider_min' => 0, |
| 355 |
'slider_max' => 180, |
| 356 |
'slider_step' => 5, |
| 357 |
) |
| 358 |
); |
| 359 |
} |
| 360 |
|
| 361 |
/** |
| 362 |
* Render Service pricing fields for the inspector. |
| 363 |
* |
| 364 |
* @return void |
| 365 |
*/ |
| 366 |
function wpbc_appointment_services_render_pricing_fields() { |
| 367 |
if ( ! wpbc_appointment_services_is_pricing_available() ) { |
| 368 |
?> |
| 369 |
<div class="inspector__row wpbc_appointment_services__pricing_upgrade"> |
| 370 |
<div class="inspector__control"> |
| 371 |
<div class="inspector__label"> |
| 372 |
<?php esc_html_e( 'Service price', 'booking' ); ?> |
| 373 |
<a class="wpbc_pro_label" href="<?php echo esc_url( 'https://wpbookingcalendar.com/features/' ); ?>" target="_blank" rel="noopener noreferrer">BS+</a> |
| 374 |
</div> |
| 375 |
<p class="description wpbc_bfb__help"><?php esc_html_e( 'Service pricing and online payments are available in Booking Calendar Business Small or higher. Service scheduling remains fully available in this edition.', 'booking' ); ?></p> |
| 376 |
</div> |
| 377 |
</div> |
| 378 |
<?php |
| 379 |
return; |
| 380 |
} |
| 381 |
|
| 382 |
wpbc_appointment_services_render_field( 'base_cost', __( 'Base price', 'booking' ), 'number', array( |
| 383 |
'min' => 0, |
| 384 |
'step' => 1, |
| 385 |
'slider' => true, |
| 386 |
'slider_min' => 0, |
| 387 |
'slider_max' => 1000, |
| 388 |
'slider_step' => 1, |
| 389 |
) ); |
| 390 |
} |
| 391 |
|
| 392 |
/** |
| 393 |
* Render the existing booking-resource choices as assigned Providers. |
| 394 |
* |
| 395 |
* @return void |
| 396 |
*/ |
| 397 |
function wpbc_appointment_services_render_provider_fields() { |
| 398 |
?> |
| 399 |
<div class="inspector__row"> |
| 400 |
<label for="resource_ids" class="inspector__label"><?php esc_html_e( 'Assigned Providers', 'booking' ); ?></label> |
| 401 |
<div class="inspector__control"> |
| 402 |
<select id="resource_ids" class="inspector__input" data-service-field="resource_ids" multiple size="6" disabled> |
| 403 |
<?php foreach ( wpbc_appointment_services_get_provider_options() as $resource_id => $title ) : ?> |
| 404 |
<option value="<?php echo absint( $resource_id ); ?>"><?php echo esc_html( $title ); ?></option> |
| 405 |
<?php endforeach; ?> |
| 406 |
</select> |
| 407 |
<p class="description wpbc_bfb__help"><?php esc_html_e( 'Select every Provider who can perform this Service. Providers use the availability of their Booking Calendar resource.', 'booking' ); ?></p> |
| 408 |
<?php wpbc_appointment_services_render_provider_admin_links( 0, 'wpbc_appointment_provider_links--inspector', false ); ?> |
| 409 |
</div> |
| 410 |
</div> |
| 411 |
<?php |
| 412 |
} |
| 413 |
|
| 414 |
/** |
| 415 |
* Render the Booking Form assignment field for the inspector. |
| 416 |
* |
| 417 |
* @return void |
| 418 |
*/ |
| 419 |
function wpbc_appointment_services_render_form_fields() { |
| 420 |
?> |
| 421 |
<div class="inspector__row"> |
| 422 |
<label for="booking_form_id" class="inspector__label"><?php esc_html_e( 'Booking Form', 'booking' ); ?></label> |
| 423 |
<div class="inspector__control"> |
| 424 |
<select id="booking_form_id" class="inspector__input" data-service-field="booking_form_id" disabled> |
| 425 |
<?php foreach ( wpbc_appointment_services_get_form_options() as $form_id => $title ) : ?> |
| 426 |
<option value="<?php echo absint( $form_id ); ?>"><?php echo esc_html( $title ); ?></option> |
| 427 |
<?php endforeach; ?> |
| 428 |
</select> |
| 429 |
</div> |
| 430 |
</div> |
| 431 |
<?php |
| 432 |
} |
| 433 |
|
| 434 |
/** |
| 435 |
* Render duplicate and archive actions for the inspector. |
| 436 |
* |
| 437 |
* @return void |
| 438 |
*/ |
| 439 |
function wpbc_appointment_services_render_advanced_fields() { |
| 440 |
?> |
| 441 |
<button type="button" class="button wpbc_appointment_services__duplicate" disabled><?php |
| 442 |
esc_html_e( 'Duplicate Service', 'booking' ); ?></button> |
| 443 |
<button type="button" class="button button-link-delete wpbc_appointment_services__archive" disabled><?php |
| 444 |
esc_html_e( 'Archive Service', 'booking' ); ?></button><?php |
| 445 |
} |
| 446 |
|
| 447 |
/** |
| 448 |
* Render the complete Service Settings inspector panel. |
| 449 |
* |
| 450 |
* @return void |
| 451 |
*/ |
| 452 |
function wpbc_appointment_services_render_settings_panel() { |
| 453 |
?> |
| 454 |
<div class="wpbc_appointment_services__operation_host" data-wpbc-appointment-services-operation-host hidden></div> |
| 455 |
<div data-wpbc-appointment-services-native-inspector> |
| 456 |
<div data-wpbc-appointment-service-inspector-header> |
| 457 |
<?php WPBC_UI_Sidebar_Panels::render_inspector_header( __( 'Service Settings', 'booking' ), __( 'Select a Service or create a new one.', 'booking' ) ); ?> |
| 458 |
</div> |
| 459 |
<?php |
| 460 |
WPBC_UI_Sidebar_Panels::render_collapsible_group( array( |
| 461 |
'id' => 'wpbc_service_general', |
| 462 |
'group' => 'service-general', |
| 463 |
'title' => __( 'General', 'booking' ), |
| 464 |
'open' => true, |
| 465 |
), 'wpbc_appointment_services_render_general_fields' ); |
| 466 |
WPBC_UI_Sidebar_Panels::render_collapsible_group( array( |
| 467 |
'id' => 'wpbc_service_scheduling', |
| 468 |
'group' => 'service-scheduling', |
| 469 |
'title' => __( 'Scheduling', 'booking' ), |
| 470 |
'open' => true, |
| 471 |
), 'wpbc_appointment_services_render_scheduling_fields' ); |
| 472 |
WPBC_UI_Sidebar_Panels::render_collapsible_group( array( |
| 473 |
'id' => 'wpbc_service_pricing', |
| 474 |
'group' => 'service-pricing', |
| 475 |
'title' => __( 'Pricing', 'booking' ), |
| 476 |
), 'wpbc_appointment_services_render_pricing_fields' ); |
| 477 |
WPBC_UI_Sidebar_Panels::render_collapsible_group( array( |
| 478 |
'id' => 'wpbc_service_providers', |
| 479 |
'group' => 'service-providers', |
| 480 |
'title' => __( 'Providers', 'booking' ), |
| 481 |
), 'wpbc_appointment_services_render_provider_fields' ); |
| 482 |
WPBC_UI_Sidebar_Panels::render_collapsible_group( array( |
| 483 |
'id' => 'wpbc_service_form', |
| 484 |
'group' => 'service-form', |
| 485 |
'title' => __( 'Booking Form', 'booking' ), |
| 486 |
), 'wpbc_appointment_services_render_form_fields' ); |
| 487 |
WPBC_UI_Sidebar_Panels::render_collapsible_group( array( |
| 488 |
'id' => 'wpbc_service_advanced', |
| 489 |
'group' => 'service-advanced', |
| 490 |
'title' => __( 'Advanced', 'booking' ), |
| 491 |
), 'wpbc_appointment_services_render_advanced_fields' ); |
| 492 |
?></div><?php |
| 493 |
} |
| 494 |
|
| 495 |
/** |
| 496 |
* Render the Service editor action in the native right-sidebar footer. |
| 497 |
* |
| 498 |
* The footer is outside the SimpleBar scrolling element so Save Service is |
| 499 |
* immediately visible and remains available while inspector widgets scroll. |
| 500 |
* JavaScript reveals it only while the Settings panel has an open editor. |
| 501 |
* |
| 502 |
* @param array $active_page_arr Active page, tab, and subtab identifiers. |
| 503 |
* |
| 504 |
* @return void |
| 505 |
*/ |
| 506 |
function wpbc_appointment_services_render_right_sidebar_footer( $active_page_arr ) { |
| 507 |
if ( |
| 508 |
empty( $active_page_arr['active_page'] ) |
| 509 |
|| 'wpbc-services' !== $active_page_arr['active_page'] |
| 510 |
|| ! wpbc_appointment_services__is_page() |
| 511 |
|| ! current_user_can( wpbc_appointment_services_get_manage_capability() ) |
| 512 |
) { |
| 513 |
return; |
| 514 |
} |
| 515 |
?> |
| 516 |
<div class="wpbc_ui_el__vert_right_bar__footer_section wpbc_appointment_services__right_sidebar_footer" hidden> |
| 517 |
<div class="wpbc_appointment_services__top_actions wpbc_appointment_services__footer_actions wpbc_ui_el__buttons_group" hidden> |
| 518 |
<button type="button" class="button wpbc_appointment_services__cancel" data-wpbc-appointment-services-cancel hidden disabled> |
| 519 |
<?php esc_html_e( 'Cancel', 'booking' ); ?> |
| 520 |
</button> |
| 521 |
<button type="button" class="button button-primary wpbc_appointment_services__save" hidden disabled> |
| 522 |
<?php esc_html_e( 'Save Service', 'booking' ); ?> |
| 523 |
</button> |
| 524 |
<button type="button" class="button button-primary wpbc_appointment_services__operation_review" hidden disabled><?php esc_html_e( 'Review changes', 'booking' ); ?></button> |
| 525 |
<button type="button" class="button button-primary wpbc_appointment_services__operation_apply" hidden disabled><?php esc_html_e( 'Apply changes', 'booking' ); ?></button> |
| 526 |
</div> |
| 527 |
</div> |
| 528 |
<?php |
| 529 |
} |
| 530 |
add_action( 'wpbc_ui__right_vertical_sidebar_footer', 'wpbc_appointment_services_render_right_sidebar_footer', 10, 1 ); |
| 531 |
|
| 532 |
/** |
| 533 |
* Render contextual help for the Service and Provider data model. |
| 534 |
* |
| 535 |
* @return void |
| 536 |
*/ |
| 537 |
function wpbc_appointment_services_render_help_panel() { |
| 538 |
WPBC_UI_Sidebar_Panels::render_inspector_header( __( 'About Services', 'booking' ), __( 'How Services work with the existing Booking Calendar architecture.', 'booking' ) ); |
| 539 |
?> |
| 540 |
<div class="wpbc_appointment_services__help"><p><?php |
| 541 |
if ( wpbc_appointment_services_is_pricing_available() ) { |
| 542 |
esc_html_e( 'A Service describes what a customer can book. Its settings define the appointment duration, preparation and recovery buffers, price, and Booking Form.', 'booking' ); |
| 543 |
} else { |
| 544 |
esc_html_e( 'A Service describes what a customer can book. Its settings define the appointment duration, preparation and recovery buffers, Providers, and Booking Form.', 'booking' ); |
| 545 |
} |
| 546 |
?></p> |
| 547 |
<h4><?php |
| 548 |
esc_html_e( 'Providers and availability', 'booking' ); ?></h4> |
| 549 |
<p><?php |
| 550 |
esc_html_e( 'Providers remain Booking Calendar resources because every Provider needs an independent availability calendar.', 'booking' ); ?></p> |
| 551 |
<ul> |
| 552 |
<li><?php |
| 553 |
esc_html_e( 'The weekday dots are a recurring weekly summary. A weekday is available when General Availability allows it and, when Working Time is enabled, the Provider has at least one working interval that day.', 'booking' ); ?></li> |
| 554 |
<li><?php |
| 555 |
esc_html_e( 'The summary does not check Seasons, date-specific days off, existing bookings, time-slot rules, Service duration, or buffers. The customer booking calendar performs those complete checks for the selected date and time.', 'booking' ); ?></li> |
| 556 |
<li><?php |
| 557 |
esc_html_e( 'When several Providers are assigned, a green dot means at least one of them is normally available that weekday. Point to the dot to see which Providers are included.', 'booking' ); ?></li> |
| 558 |
<li><?php |
| 559 |
esc_html_e( 'Use the Provider initials below the dots to open that Provider\'s Working Time settings directly.', 'booking' ); ?></li> |
| 560 |
</ul> |
| 561 |
<h4><?php |
| 562 |
esc_html_e( 'Getting started', 'booking' ); ?></h4> |
| 563 |
<ol> |
| 564 |
<li><?php |
| 565 |
esc_html_e( 'Add a Service and enter its duration.', 'booking' ); ?></li> |
| 566 |
<li><?php |
| 567 |
esc_html_e( 'Assign at least one Provider.', 'booking' ); ?></li> |
| 568 |
<li><?php |
| 569 |
esc_html_e( 'Select a Booking Form and save the Service.', 'booking' ); ?></li> |
| 570 |
<li><?php |
| 571 |
esc_html_e( 'Add the [booking_appointment] shortcode to the page where customers should make Appointments.', 'booking' ); ?></li> |
| 572 |
</ol> |
| 573 |
<h4><?php esc_html_e( 'Appointment shortcode', 'booking' ); ?></h4> |
| 574 |
<p><code>[booking_appointment]</code></p> |
| 575 |
<p><?php esc_html_e( 'Optional parameters:', 'booking' ); ?></p> |
| 576 |
<ul> |
| 577 |
<li><code>service_id</code>, <code>provider_id</code> — <?php esc_html_e( 'restrict the flow to one Service or Provider.', 'booking' ); ?></li> |
| 578 |
<li><code>services</code>, <code>providers</code> — <?php esc_html_e( 'comma-separated lists of allowed Service or Provider IDs; Services follow the supplied order.', 'booking' ); ?></li> |
| 579 |
<li><code>form_type</code> — <?php esc_html_e( 'override the Booking Form selected in the Service.', 'booking' ); ?></li> |
| 580 |
<li><code>nummonths</code>, <code>startmonth</code> — <?php esc_html_e( 'set the calendar count and initial month.', 'booking' ); ?></li> |
| 581 |
<li><code>calendar_dates_start</code>, <code>calendar_dates_end</code> — <?php esc_html_e( 'limit the calendar to an inclusive date range in YYYY-MM-DD format.', 'booking' ); ?></li> |
| 582 |
<li><code>options</code> — <?php esc_html_e( 'pass supported native Booking Calendar options.', 'booking' ); ?></li> |
| 583 |
<li><code>auto_select_provider</code> — <?php esc_html_e( 'use "on" to skip the Provider step when only one compatible Provider exists; disabled by default.', 'booking' ); ?></li> |
| 584 |
<li><code>show_progress</code> — <?php esc_html_e( 'use "off" to hide the numbered Appointment progress line.', 'booking' ); ?></li> |
| 585 |
<li><code>progress_item_1_title</code> through <code>progress_item_3_title</code> — <?php esc_html_e( 'replace the three titles shown below the progress numbers; use an empty value to hide a title.', 'booking' ); ?></li> |
| 586 |
<li><code>progress_item_1_number</code> through <code>progress_item_3_number</code> — <?php esc_html_e( 'replace the displayed progress numbers; use an empty value to hide a number.', 'booking' ); ?></li> |
| 587 |
<li><code>screen_1_title</code>, <code>screen_1_description</code>, <code>screen_2_title</code>, <code>screen_2_description</code> — <?php esc_html_e( 'replace the Service and Provider screen headings; use an empty value to hide an element.', 'booking' ); ?></li> |
| 588 |
<li><code>allow_past</code> — <?php esc_html_e( 'use "on" to allow administrators or controlled workflows to select past dates.', 'booking' ); ?></li> |
| 589 |
</ul> |
| 590 |
<p><code>[booking_appointment service_id="12" provider_id="5"]</code></p> |
| 591 |
<p><code>[booking_appointment progress_item_1_title="" progress_item_1_number="01/03" progress_item_2_title="Specialist" progress_item_3_title="Schedule & Details"]</code></p> |
| 592 |
</div><?php |
| 593 |
} |
| 594 |
|