PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.2.4
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.2.4
5.6.8 5.6.7 5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / helpers / calendar_helper.php
latepoint / lib / helpers Last commit date
activities_helper.php 1 year ago agent_helper.php 1 year ago auth_helper.php 9 months ago blocks_helper.php 1 year ago booking_helper.php 1 year ago bricks_helper.php 1 year ago bundles_helper.php 1 year ago calendar_helper.php 9 months ago carts_helper.php 1 year ago connector_helper.php 9 months ago csv_helper.php 9 months ago customer_helper.php 9 months ago customer_import_helper.php 9 months ago database_helper.php 9 months ago debug_helper.php 1 year ago defaults_helper.php 1 year ago elementor_helper.php 1 year ago email_helper.php 9 months ago encrypt_helper.php 1 year ago events_helper.php 9 months ago form_helper.php 9 months ago icalendar_helper.php 1 year ago image_helper.php 1 year ago invoices_helper.php 9 months ago license_helper.php 1 year ago location_helper.php 1 year ago marketing_systems_helper.php 1 year ago meeting_systems_helper.php 1 year ago menu_helper.php 9 months ago meta_helper.php 1 year ago migrations_helper.php 1 year ago money_helper.php 1 year ago notifications_helper.php 9 months ago order_intent_helper.php 9 months ago orders_helper.php 1 year ago otp_helper.php 9 months ago pages_helper.php 1 year ago params_helper.php 1 year ago payments_helper.php 1 year ago price_breakdown_helper.php 1 year ago process_jobs_helper.php 1 year ago processes_helper.php 1 year ago replacer_helper.php 1 year ago resource_helper.php 1 year ago roles_helper.php 9 months ago router_helper.php 1 year ago service_helper.php 1 year ago sessions_helper.php 1 year ago settings_helper.php 9 months ago short_links_systems_helper.php 9 months ago shortcodes_helper.php 9 months ago sms_helper.php 1 year ago steps_helper.php 9 months ago stripe_connect_helper.php 9 months ago styles_helper.php 9 months ago support_topics_helper.php 1 year ago time_helper.php 9 months ago timeline_helper.php 9 months ago transaction_helper.php 1 year ago transaction_intent_helper.php 1 year ago util_helper.php 9 months ago version_specific_updates_helper.php 9 months ago whatsapp_helper.php 1 year ago work_periods_helper.php 1 year ago wp_datetime.php 1 year ago wp_user_helper.php 1 year ago
calendar_helper.php
575 lines
1 <?php
2 /*
3 * Copyright (c) 2022 LatePoint LLC. All rights reserved.
4 */
5
6
7 class OsCalendarHelper {
8
9 public static function generate_dates_and_times_picker(OsBookingModel $booking, OsWpDateTime $target_date, $auto_search = false, array $calendar_settings = []) : string{
10 $is_recurring_supported = apply_filters('latepoint_is_feature_recurring_bookings_on', false);
11 $can_service_be_recurring = $is_recurring_supported && ($booking->service->get_meta_by_key('allow_recurring_bookings') == 'on');
12 ob_start(); ?>
13 <div class="os-dates-and-times-w <?php echo $auto_search ? 'auto-search is-searching' : '' ; ?> calendar-style-<?php echo OsStepsHelper::get_calendar_style(); ?>" data-route="<?php echo esc_attr(OsRouterHelper::build_route_name('steps', 'load_datepicker_month')); ?>" data-allow-recurring="<?php echo $can_service_be_recurring ? 'yes' : 'no'; ?>">
14 <div class="os-dates-w" data-time-pick-style="<?php echo esc_attr(OsStepsHelper::get_time_pick_style()); ?>">
15 <?php if($auto_search){ ?>
16 <div class="os-calendar-searching-info"><?php echo sprintf(esc_html__( 'Searching %s for available dates', 'latepoint' ), '<span></span>'); ?></div>
17 <?php } ?>
18 <div class="os-calendar-while-searching-wrapper">
19 <?php OsCalendarHelper::generate_calendar_for_datepicker_step( \LatePoint\Misc\BookingRequest::create_from_booking_model( $booking ), $target_date, $calendar_settings ); ?>
20 </div>
21 </div>
22
23 <div class="time-selector-w <?php echo OsStepsHelper::hide_unavailable_slots() ? 'hide-not-available-slots' : ''; ?> <?php echo 'time-system-' . esc_attr(OsTimeHelper::get_time_system()); ?> <?php echo ( OsSettingsHelper::is_on( 'show_booking_end_time' ) ) ? 'with-end-time' : 'without-end-time'; ?> style-<?php echo esc_attr(OsStepsHelper::get_time_pick_style()); ?>">
24 <div class="times-header">
25 <div class="th-line"></div>
26 <div class="times-header-label">
27 <?php esc_html_e( 'Pick a slot for', 'latepoint' ); ?> <span></span>
28 <?php do_action( 'latepoint_step_datepicker_appointment_time_header_label', $booking, $calendar_settings ); ?>
29 </div>
30 <div class="th-line"></div>
31 </div>
32 <div class="os-times-w">
33 <div class="timeslots"></div>
34 </div>
35 </div>
36 <?php do_action( 'latepoint_dates_and_times_picker_after', $booking, $target_date, $calendar_settings ); ?>
37 </div>
38 <?php
39 $html = ob_get_clean();
40 return $html;
41 }
42
43 /**
44 * Get list of statuses which should not appear on calendar
45 *
46 * @return array
47 */
48 public static function get_booking_statuses_hidden_from_calendar(): array {
49 $statuses = explode( ',', OsSettingsHelper::get_settings_value( 'calendar_hidden_statuses', '' ) );
50
51 /**
52 * Get list of statuses which bookings should not appear on calendar
53 *
54 * @param {array} $statuses array of status codes that will be hidden from calendar
55 * @returns {array} The filtered array of status codes
56 *
57 * @since 4.7.0
58 * @hook latepoint_get_booking_statuses_hidden_from_calendar
59 *
60 */
61 return apply_filters( 'latepoint_get_booking_statuses_hidden_from_calendar', $statuses );
62 }
63
64
65 /**
66 * Returns an array of booking status codes to be displayed on calendar
67 *
68 * @return {array} The array of statuses
69 */
70 public static function get_booking_statuses_to_display_on_calendar(): array {
71 $hidden_statuses = self::get_booking_statuses_hidden_from_calendar();
72 $all_statuses = OsBookingHelper::get_statuses_list();
73 $eligible_statuses = [];
74 foreach ( $all_statuses as $status_code => $status_label ) {
75 if ( ! in_array( $status_code, $hidden_statuses ) ) {
76 $eligible_statuses[] = $status_code;
77 }
78 }
79
80 /**
81 * Returns an array of booking status codes to be displayed on calendar
82 *
83 * @param {array} array of statuses
84 *
85 * @returns {array} The array of statuses
86 *
87 * @since 4.7.0
88 * @hook latepoint_get_booking_statuses_to_display_on_calendar
89 *
90 */
91 return apply_filters( 'latepoint_get_booking_statuses_to_display_on_calendar', $eligible_statuses );
92 }
93
94 public static function is_external_calendar_enabled( string $external_calendar_code ): bool {
95 return OsSettingsHelper::is_on( 'enable_' . $external_calendar_code );
96 }
97
98 public static function get_list_of_external_calendars( $enabled_only = false ) {
99 $external_calendars = [];
100
101 /**
102 * Returns an array of external calendars
103 *
104 * @param {array} array of calendars
105 * @param {bool} filter to return only calendars that are enabled
106 *
107 * @returns {array} The array of external calendars
108 *
109 * @since 4.7.0
110 * @hook latepoint_list_of_external_calendars
111 *
112 */
113 return apply_filters( 'latepoint_list_of_external_calendars', $external_calendars, $enabled_only );
114 }
115
116
117 /**
118 * @param \LatePoint\Misc\BookingRequest $booking_request
119 * @param DateTime $target_date
120 * @param array $settings
121 *
122 * @return void
123 */
124 public static function generate_calendar_for_datepicker_step( \LatePoint\Misc\BookingRequest $booking_request, DateTime $target_date, array $settings = [] ) {
125 $defaults = [
126 'exclude_booking_ids' => [],
127 'number_of_months_to_preload' => 1,
128 'timezone_name' => false,
129 'layout' => 'classic',
130 'highlight_target_date' => false,
131 'consider_cart_items' => false,
132 'output_target_date_in_header' => false
133 ];
134
135 $settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
136
137 $weekdays = OsBookingHelper::get_weekdays_arr();
138 $today_date = new OsWpDateTime( 'today' );
139
140
141 ?>
142 <div class="os-current-month-label-w calendar-mobile-controls">
143 <div class="os-current-month-label">
144 <div class="current-month">
145 <?php if ( $settings['highlight_target_date'] && $settings['output_target_date_in_header'] ) {
146 echo esc_html( OsTimeHelper::get_nice_date_with_optional_year( $target_date->format( 'Y-m-d' ), false ) );
147 } else {
148 echo esc_html( OsUtilHelper::get_month_name_by_number( $target_date->format( 'n' ) ) );
149 } ?>
150 </div>
151 <div class="current-year"><?php echo esc_html( $target_date->format( 'Y' ) ); ?></div>
152 </div>
153 <div class="os-month-control-buttons-w">
154 <button type="button" class="os-month-prev-btn" data-route="<?php echo esc_attr( OsRouterHelper::build_route_name( 'steps', 'load_datepicker_month' ) ); ?>">
155 <i class="latepoint-icon latepoint-icon-arrow-left"></i></button>
156 <?php if ( $settings['layout'] == 'horizontal' ) {
157 echo '<button class="latepoint-btn latepoint-btn-outline os-month-today-btn" data-year="' . esc_attr( $today_date->format( 'Y' ) ) . '" data-month="' . esc_attr( $today_date->format( 'n' ) ) . '" data-date="' . esc_attr( $today_date->format( 'Y-m-d' ) ) . '">' . esc_html__( 'Today', 'latepoint' ) . '</button>';
158 } ?>
159 <button type="button" class="os-month-next-btn" data-route="<?php echo esc_attr( OsRouterHelper::build_route_name( 'steps', 'load_datepicker_month' ) ); ?>">
160 <i class="latepoint-icon latepoint-icon-arrow-right"></i></button>
161 </div>
162 </div>
163 <?php if ( $settings['layout'] == 'classic' ) { ?>
164 <div class="os-weekdays">
165 <?php
166 $start_of_week = OsSettingsHelper::get_start_of_week();
167
168 // Output the divs for each weekday
169 for ( $i = $start_of_week - 1; $i < $start_of_week - 1 + 7; $i ++ ) {
170 // Calculate the index within the range of 0-6
171 $index = $i % 7;
172
173 // Output the div for the current weekday
174 echo '<div class="weekday weekday-' . esc_attr( $index + 1 ) . '">' . esc_html( OsUtilHelper::get_first_chars($weekdays[ $index ], 1) ) . '</div>';
175 }
176 ?>
177 </div>
178 <?php } ?>
179 <div class="os-months">
180 <?php
181 $month_settings = [
182 'active' => true,
183 'timezone_name' => $settings['timezone_name'],
184 'highlight_target_date' => $settings['highlight_target_date'],
185 'exclude_booking_ids' => $settings['exclude_booking_ids'],
186 'consider_cart_items' => $settings['consider_cart_items']
187 ];
188
189 // if it's not from admin - blackout dates that are not available to select due to date restrictions in settings
190 $month_settings['earliest_possible_booking'] = OsSettingsHelper::get_earliest_possible_booking_restriction($booking_request->service_id);
191 $month_settings['latest_possible_booking'] = OsSettingsHelper::get_latest_possible_booking_restriction($booking_request->service_id);
192
193 OsCalendarHelper::generate_single_month( $booking_request, $target_date, $month_settings );
194 for ( $i = 1; $i <= $settings['number_of_months_to_preload']; $i ++ ) {
195 $next_month_target_date = clone $target_date;
196 $next_month_target_date->modify( 'first day of next month' );
197 $month_settings['active'] = false;
198 $month_settings['highlight_target_date'] = false;
199 OsCalendarHelper::generate_single_month( $booking_request, $next_month_target_date, $month_settings );
200 }
201 ?>
202 </div><?php
203 /**
204 * Fired after a datepicker calendar months are generated
205 *
206 * @param {BookingRequest} $booking_request instance of a booking request
207 * @param {DateTime} $target_date target date that is being loaded
208 * @param {array} $settings array of settings for the calendar
209 *
210 * @since 5.1.7
211 * @hook latepoint_after_datepicker_months
212 *
213 */
214 do_action('latepoint_after_datepicker_months', $booking_request, $target_date, $settings);
215 }
216
217 public static function generate_single_month( \LatePoint\Misc\BookingRequest $booking_request, DateTime $target_date, array $settings = [] ) {
218 $defaults = [
219 'accessed_from_backend' => false,
220 'active' => false,
221 'layout' => 'classic',
222 'highlight_target_date' => false,
223 'timezone_name' => false,
224 'earliest_possible_booking' => false,
225 'latest_possible_booking' => false,
226 'exclude_booking_ids' => [],
227 'consider_cart_items' => false,
228 'hide_slot_availability_count' => OsStepsHelper::hide_slot_availability_count()
229 ];
230 $settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
231
232
233 // set service to the first available if not set
234 // IMPORTANT, we have to have service in the booking request, otherwise we can't know duration and intervals
235 $service = new OsServiceModel();
236 $service = $service->where( [ 'id' => $booking_request->service_id ] )->set_limit( 1 )->get_results_as_models();
237 if ( $service ) {
238 if ( ! $booking_request->duration ) {
239 $booking_request->duration = $service->duration;
240 }
241 $selectable_time_interval = $service->get_timeblock_interval();
242 } else {
243 echo '<div class="latepoint-message latepoint-message-error">' . esc_html__( 'In order to generate the calendar, a service must be selected.', 'latepoint' ) . '</div>';
244
245 return;
246 }
247
248
249 # Get bounds for a month of a targetted day
250 $calendar_start = clone $target_date;
251 $calendar_start->modify( 'first day of this month' );
252 $calendar_end = clone $target_date;
253 $calendar_end->modify( 'last day of this month' );
254
255
256 // if it's a classic layout - it means we need to load some days from previous and next month, to fill in blank spaces on the grid
257 if ( $settings['layout'] == 'classic' ) {
258 $weekday_for_first_day_of_month = intval( $calendar_start->format( 'N' ) );
259 $weekday_for_last_day_of_month = intval( $calendar_end->format( 'N' ) );
260
261 $week_starts_on = OsSettingsHelper::get_start_of_week();
262 $week_ends_on = $week_starts_on > 1 ? $week_starts_on - 1 : 7;
263
264 if ( $weekday_for_first_day_of_month != $week_starts_on ) {
265 $days_to_subtract = ( $weekday_for_first_day_of_month - $week_starts_on + 7 ) % 7;
266 if($days_to_subtract > 0){
267 $calendar_start->modify( '-' . $days_to_subtract . ' days' );
268 }
269 }
270
271 if ( $weekday_for_last_day_of_month != $week_ends_on ) {
272 $days_to_add = ( $weekday_for_last_day_of_month > $week_ends_on ) ? abs( 7 - $weekday_for_last_day_of_month + $week_ends_on ) : ( $week_ends_on - $weekday_for_last_day_of_month );
273 if($days_to_add > 0){
274 $calendar_end->modify( '+' . $days_to_add . ' days' );
275 }
276 }
277 }
278
279 $now_datetime = OsTimeHelper::now_datetime_object();
280
281 // figure out when the earliest and latest bookings can be placed
282 try{
283 $earliest_possible_booking = ( $settings['earliest_possible_booking'] ) ? new OsWpDateTime( $settings['earliest_possible_booking'] ) : clone $now_datetime;
284 $latest_possible_booking = ( $settings['latest_possible_booking'] ) ? new OsWpDateTime( $settings['latest_possible_booking'] ) : clone $calendar_end;
285 }catch(Exception $e){
286
287 }
288 // make sure they are set correctly
289 if ( empty($earliest_possible_booking) ) {
290 $earliest_possible_booking = clone $now_datetime;
291 }
292 if ( empty($latest_possible_booking) ) {
293 $latest_possible_booking = clone $calendar_end;
294 }
295
296 $date_range_start = ( $calendar_start->format( 'Y-m-d' ) > $earliest_possible_booking->format( 'Y-m-d' ) ) ? clone $calendar_start : clone $earliest_possible_booking;
297 $date_range_end = ( $calendar_end->format( 'Y-m-d' ) < $latest_possible_booking->format( 'Y-m-d' ) ) ? clone $calendar_end : clone $latest_possible_booking;
298
299 // make sure date range is within the requested calendar range
300 if ( ( $date_range_start->format( 'Y-m-d' ) >= $calendar_start->format( 'Y-m-d' ) )
301 && ( $date_range_end->format( 'Y-m-d' ) <= $calendar_end->format( 'Y-m-d' ) )
302 && ( $date_range_start->format( 'Y-m-d' ) <= $date_range_end->format( 'Y-m-d' ) ) ) {
303 $daily_resources = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $date_range_start, $date_range_end, [
304 'accessed_from_backend' => $settings['accessed_from_backend'],
305 'exclude_booking_ids' => $settings['exclude_booking_ids'],
306 'consider_cart_items' => $settings['consider_cart_items'],
307 'timezone_name' => $settings['timezone_name'],
308 ] );
309 } else {
310 $daily_resources = [];
311 }
312
313 $active_class = $settings['active'] ? 'active' : '';
314 $hide_single_slot_class = OsStepsHelper::hide_timepicker_when_one_slot_available() ? 'hide-if-single-slot' : '';
315 echo '<div class="os-monthly-calendar-days-w ' . esc_attr( $hide_single_slot_class . ' ' . $active_class ) . '" data-calendar-layout="' . esc_attr( $settings['layout'] ) . '" data-calendar-year="' . esc_attr( $target_date->format( 'Y' ) ) . '" data-calendar-month="' . esc_attr( $target_date->format( 'n' ) ) . '" data-calendar-month-label="' . esc_attr( OsUtilHelper::get_month_name_by_number( $target_date->format( 'n' ) ) ) . '">';
316 echo '<div class="os-monthly-calendar-days">';
317 // DAYS LOOP START
318 for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
319 if ( ! isset( $daily_resources[ $day_date->format( 'Y-m-d' ) ] ) ) {
320 $daily_resources[ $day_date->format( 'Y-m-d' ) ] = [];
321 }
322
323 $is_today = ( $day_date->format( 'Y-m-d' ) == $now_datetime->format( 'Y-m-d' ) );
324 $is_day_in_past = ( $day_date->format( 'Y-m-d' ) < $now_datetime->format( 'Y-m-d' ) );
325 $is_target_month = ( $day_date->format( 'Ym' ) == $target_date->format( 'Ym' ) );
326 $is_next_month = ( $day_date->format( 'Ym' ) > $target_date->format( 'Ym' ) );
327 $is_prev_month = ( $day_date->format( 'Ym' ) < $target_date->format( 'Ym' ) );
328 $not_in_allowed_period = false;
329
330 if ( $day_date->format( 'Y-m-d' ) < $earliest_possible_booking->format( 'Y-m-d' ) ) {
331 $not_in_allowed_period = true;
332 }
333 if ( $day_date->format( 'Y-m-d' ) > $latest_possible_booking->format( 'Y-m-d' ) ) {
334 $not_in_allowed_period = true;
335 }
336
337 $work_minutes = [];
338
339 if($settings['accessed_from_backend'] || !$not_in_allowed_period ){
340 // only do this if is in allowed period or accessed from backend
341 foreach ( $daily_resources[ $day_date->format( 'Y-m-d' ) ] as $resource ) {
342 if ( $is_day_in_past && $not_in_allowed_period ) {
343 continue;
344 }
345 $work_minutes = array_merge( $work_minutes, $resource->work_minutes );
346 }
347 $work_minutes = array_unique( $work_minutes, SORT_NUMERIC );
348 sort( $work_minutes, SORT_NUMERIC );
349 }
350
351
352
353 $work_boundaries = OsResourceHelper::get_work_boundaries_for_resources( $daily_resources[ $day_date->format( 'Y-m-d' ) ] );
354 $total_work_minutes = $work_boundaries->end_time - $work_boundaries->start_time;
355
356 $booking_slots = OsResourceHelper::get_ordered_booking_slots_from_resources( $daily_resources[ $day_date->format( 'Y-m-d' ) ] );
357
358 $bookable_minutes = [];
359 if($settings['accessed_from_backend'] || !$not_in_allowed_period ) {
360 // only do this if is in allowed period or accessed from backend
361 foreach ( $booking_slots as $booking_slot ) {
362 if ( $booking_slot->can_accomodate( $booking_request->total_attendees ) ) {
363 $bookable_minutes[ $booking_slot->start_time ] = isset( $bookable_minutes[ $booking_slot->start_time ] ) ? max( $booking_slot->available_capacity(), $bookable_minutes[ $booking_slot->start_time ] ) : $booking_slot->available_capacity();
364 }
365 }
366 ksort( $bookable_minutes );
367 }
368 $bookable_minutes_with_capacity_data = '';
369 // this is a group service
370 if ( $service->is_group_service() && ! $settings['hide_slot_availability_count'] ) {
371 foreach ( $bookable_minutes as $minute => $available_capacity ) {
372 $bookable_minutes_with_capacity_data .= $minute . ':' . $available_capacity . ',';
373 }
374 } else {
375 foreach ( $bookable_minutes as $minute => $available_capacity ) {
376 $bookable_minutes_with_capacity_data .= $minute . ',';
377 }
378 }
379 $bookable_minutes_with_capacity_data = rtrim( $bookable_minutes_with_capacity_data, ',' );
380
381
382 $bookable_slots_count = count( $bookable_minutes );
383 // TODO use work minutes instead to calculate minimum gap
384 $minimum_slot_gap = \LatePoint\Misc\BookingSlot::find_minimum_gap_between_slots( $booking_slots );
385 $day_class = 'os-day os-day-current week-day-' . strtolower( $day_date->format( 'N' ) );
386 $tabbable = true;
387 if ( empty( $bookable_minutes ) ) {
388 $day_class .= ' os-not-available';
389 $tabbable = false;
390 }
391 if ( $is_today ) {
392 $day_class .= ' os-today';
393 }
394 if ( $is_day_in_past ) {
395 $day_class .= ' os-day-passed';
396 $tabbable = false;
397 }
398 if ( $is_target_month ) {
399 $day_class .= ' os-month-current';
400 }
401 if ( $is_next_month ) {
402 $day_class .= ' os-month-next';
403 }
404 if ( $is_prev_month ) {
405 $day_class .= ' os-month-prev';
406 }
407 if ( $not_in_allowed_period ) {
408 $day_class .= ' os-not-in-allowed-period';
409 $tabbable = false;
410 }
411 if ( count( $bookable_minutes ) == 1 && OsStepsHelper::hide_timepicker_when_one_slot_available() ) {
412 $day_class .= ' os-one-slot-only';
413 }
414 if ( ( $day_date->format( 'Y-m-d' ) == $target_date->format( 'Y-m-d' ) ) && $settings['highlight_target_date'] ) {
415 $day_class .= ' selected';
416 }
417 ?>
418
419 <div <?php if($tabbable) echo 'tabindex="0"'; ?> role="button" class="<?php echo esc_attr( $day_class ); ?>"
420 data-date="<?php echo esc_attr( $day_date->format( 'Y-m-d' ) ); ?>"
421 data-nice-date="<?php echo esc_attr( OsTimeHelper::get_nice_date_with_optional_year( $day_date->format( 'Y-m-d' ), false ) ); ?>"
422 data-service-duration="<?php echo esc_attr( $booking_request->duration ); ?>"
423 data-total-work-minutes="<?php echo esc_attr( $total_work_minutes ); ?>"
424 data-work-start-time="<?php echo esc_attr( $work_boundaries->start_time ); ?>"
425 data-work-end-time="<?php echo esc_attr( $work_boundaries->end_time ); ?>"
426 data-bookable-minutes="<?php echo esc_attr( $bookable_minutes_with_capacity_data ); ?>"
427 data-work-minutes="<?php echo esc_attr( implode( ',', $work_minutes ) ); ?>"
428 data-interval="<?php echo esc_attr( $selectable_time_interval ); ?>">
429 <?php if ( $settings['layout'] == 'horizontal' ) {
430 $weekday = esc_html( OsBookingHelper::get_weekday_name_by_number( $day_date->format( 'N' )));
431 ?>
432 <div class="os-day-weekday"><?php echo OsUtilHelper::get_first_chars($weekday, 1); ?></div>
433 <?php } ?>
434 <div class="os-day-box">
435 <?php
436 if ( $bookable_slots_count && ! $settings['hide_slot_availability_count'] ) {
437 // translators: %d is the number of slots available
438 echo '<div class="os-available-slots-tooltip">' . esc_html( sprintf( __( '%d Available', 'latepoint' ), $bookable_slots_count ) ) . '</div>';
439 } ?>
440 <div class="os-day-number"><?php echo esc_html( $day_date->format( 'j' ) ); ?></div>
441 <?php if ( ! $is_day_in_past && ! $not_in_allowed_period ) { ?>
442 <div class="os-day-status">
443 <?php
444 if ( $total_work_minutes > 0 && $bookable_slots_count ) {
445 $available_blocks_count = 0;
446 $not_available_started_count = 0;
447 $duration = $booking_request->duration;
448 $end_time = $work_boundaries->end_time - $duration;
449 $processed_count = 0;
450 $last_available_slot_time = false;
451 $bookable_ranges = [];
452 $loop_availability_status = false;
453 for ( $i = 0; $i < count( $booking_slots ); $i ++ ) {
454 if ( $booking_slots[ $i ]->can_accomodate( $booking_request->total_attendees ) ) {
455 // AVAILABLE SLOT
456 if ( $loop_availability_status && $i > 0 && ( ( $booking_slots[ $i ]->start_time - $booking_slots[ $i - 1 ]->start_time ) > $minimum_slot_gap ) ) {
457 // big gap between previous slot and this slot
458 $bookable_ranges[] = $booking_slots[ $i - 1 ]->start_time + $minimum_slot_gap;
459 $bookable_ranges[] = $booking_slots[ $i ]->start_time;
460 }
461 if ( ! $loop_availability_status ) {
462 $bookable_ranges[] = $booking_slots[ $i ]->start_time;
463 }
464 $last_available_slot_time = $booking_slots[ $i ]->start_time;
465 $loop_availability_status = true;
466 } else {
467 // NOT AVAILABLE
468 // a different resource but with the same start time, so that if its available (checked in next loop iteration) - we don't block this slot
469 if ( isset( $booking_slots[ $i + 1 ] ) && $booking_slots[ $i + 1 ]->start_time == $booking_slots[ $i ]->start_time ) {
470 continue;
471 }
472 // check if last available slot had the same start time as current one, if so - we don't block this slot and move to the next one
473 if ( $last_available_slot_time == $booking_slots[ $i ]->start_time && isset( $booking_slots[ $i - 1 ] ) && $booking_slots[ $i - 1 ]->start_time == $booking_slots[ $i ]->start_time ) {
474 continue;
475 }
476 // if last available slot exists and previous slot was also available
477 if ( $last_available_slot_time && $loop_availability_status ) {
478 $bookable_ranges[] = $last_available_slot_time + $minimum_slot_gap;
479 }
480 $loop_availability_status = false;
481 }
482 }
483 if ( $bookable_ranges ) {
484 for ( $i = 0; $i < count( $bookable_ranges ); $i += 2 ) {
485 $left = ( $bookable_ranges[ $i ] - $work_boundaries->start_time ) / $total_work_minutes * 100;
486 $width = isset( $bookable_ranges[ $i + 1 ] ) ? ( ( $bookable_ranges[ $i + 1 ] - $bookable_ranges[ $i ] ) / $total_work_minutes * 100 ) : ( ( $work_boundaries->end_time - $bookable_ranges[ $i ] ) / $total_work_minutes * 100 );
487 echo '<div class="day-available" style="left:' . esc_attr( $left ) . '%;width:' . esc_attr( $width ) . '%;"></div>';
488 }
489 }
490 }
491 ?>
492 </div>
493 <?php } ?>
494 </div>
495 </div>
496
497 <?php
498
499 // DAYS LOOP END
500 }
501 echo '</div></div>';
502 }
503
504 // Used on holiday/custom schedule generator lightbox
505 public static function generate_monthly_calendar_days_only( $target_date_string = 'today', $highlight_target_date = false, bool $is_active = false ) {
506 $target_date = new OsWpDateTime( $target_date_string );
507 $calendar_start = clone $target_date;
508 $calendar_start->modify( 'first day of this month' );
509 $calendar_end = clone $target_date;
510 $calendar_end->modify( 'last day of this month' );
511
512 $weekday_for_first_day_of_month = $calendar_start->format( 'N' ) - 1;
513 $weekday_for_last_day_of_month = $calendar_end->format( 'N' ) - 1;
514
515
516 if ( $weekday_for_first_day_of_month > 0 ) {
517 $calendar_start->modify( '-' . $weekday_for_first_day_of_month . ' days' );
518 }
519
520 if ( $weekday_for_last_day_of_month < 6 ) {
521 $days_to_add = 6 - $weekday_for_last_day_of_month;
522 if($days_to_add > 0){
523 $calendar_end->modify( '+' . $days_to_add . ' days' );
524 }
525 }
526
527 $active_class = $is_active ? 'active' : '';
528
529 echo '<div class="os-monthly-calendar-days-w '.$active_class.'" data-calendar-year="' . esc_attr( $target_date->format( 'Y' ) ) . '" data-calendar-month="' . esc_attr( $target_date->format( 'n' ) ) . '" data-calendar-month-label="' . esc_attr( OsUtilHelper::get_month_name_by_number( $target_date->format( 'n' ) ) ) . '">';
530 echo '<div class="os-monthly-calendar-days">';
531 for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
532 $is_today = ( $day_date->format( 'Y-m-d' ) == OsTimeHelper::today_date() ) ? true : false;
533 $is_day_in_past = ( $day_date->format( 'Y-m-d' ) < OsTimeHelper::today_date() ) ? true : false;
534 $day_class = 'os-day os-day-current week-day-' . strtolower( $day_date->format( 'N' ) );
535
536 if ( $day_date->format( 'm' ) > $target_date->format( 'm' ) ) {
537 $day_class .= ' os-month-next';
538 }
539 if ( $day_date->format( 'm' ) < $target_date->format( 'm' ) ) {
540 $day_class .= ' os-month-prev';
541 }
542
543 if ( $is_today ) {
544 $day_class .= ' os-today';
545 }
546 if ( $highlight_target_date && ( $day_date->format( 'Y-m-d' ) == $target_date->format( 'Y-m-d' ) ) ) {
547 $day_class .= ' selected';
548 }
549 if ( $is_day_in_past ) {
550 $day_class .= ' os-day-passed';
551 } ?>
552 <div class="<?php echo esc_attr( $day_class ); ?>" data-date="<?php echo esc_attr( $day_date->format( 'Y-m-d' ) ); ?>">
553 <div class="os-day-box">
554 <div class="os-day-number"><?php echo esc_html( $day_date->format( 'j' ) ); ?></div>
555 </div>
556 </div><?php
557 }
558 echo '</div></div>';
559 }
560
561 public static function generate_calendar_quick_actions_link( OsWpDateTime $day_date, array $settings = [] ) : string {
562 $defaults = [
563 'agent_id' => 0,
564 'location_id' => 0,
565 'service_id' => 0,
566 'start_time' => 600
567 ];
568
569 $settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
570
571 return '<a href="#" data-os-after-call="latepoint_init_calendar_quick_actions" data-os-lightbox-classes="width-400" class="day-action-trigger" data-os-output-target="lightbox" data-os-params="'.OsUtilHelper::build_os_params(['target_date' => $day_date->format('Y-m-d'), 'start_time' => $settings['start_time'], 'agent_id' => $settings['agent_id'], 'location_id' => $settings['location_id'], 'service_id' => $settings['service_id']]).'" data-os-action="'.OsRouterHelper::build_route_name('calendars', 'quick_actions').'"><i class="latepoint-icon latepoint-icon-more-horizontal"></i></a>';
572 }
573
574
575 }