PluginProbe
Booking Calendar / 11.6.1
Booking Calendar v11.6.1
11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 10.11.2 All 203 releases
booking / includes / page-appointment-services / appointment_services__ui.php

appointment_services__ui.php in Booking Calendar 11.6.1, at includes/page-appointment-services/appointment_services__ui.php

594 lines 27.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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> &mdash; <?php esc_html_e( 'restrict the flow to one Service or Provider.', 'booking' ); ?></li>
578 <li><code>services</code>, <code>providers</code> &mdash; <?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> &mdash; <?php esc_html_e( 'override the Booking Form selected in the Service.', 'booking' ); ?></li>
580 <li><code>nummonths</code>, <code>startmonth</code> &mdash; <?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> &mdash; <?php esc_html_e( 'limit the calendar to an inclusive date range in YYYY-MM-DD format.', 'booking' ); ?></li>
582 <li><code>options</code> &mdash; <?php esc_html_e( 'pass supported native Booking Calendar options.', 'booking' ); ?></li>
583 <li><code>auto_select_provider</code> &mdash; <?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> &mdash; <?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> &mdash; <?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> &mdash; <?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> &mdash; <?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> &mdash; <?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=&quot;12&quot; provider_id=&quot;5&quot;]</code></p>
591 <p><code>[booking_appointment progress_item_1_title=&quot;&quot; progress_item_1_number=&quot;01/03&quot; progress_item_2_title=&quot;Specialist&quot; progress_item_3_title=&quot;Schedule &amp; Details&quot;]</code></p>
592 </div><?php
593 }
594