PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.1.8
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.1.8
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 1 year 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 1 year ago carts_helper.php 1 year ago connector_helper.php 1 year ago csv_helper.php 1 year ago customer_helper.php 1 year ago database_helper.php 1 year ago debug_helper.php 1 year ago defaults_helper.php 1 year ago elementor_helper.php 1 year ago email_helper.php 1 year ago encrypt_helper.php 1 year ago events_helper.php 1 year ago form_helper.php 1 year ago icalendar_helper.php 1 year ago image_helper.php 1 year ago invoices_helper.php 1 year 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 1 year ago meta_helper.php 1 year ago migrations_helper.php 1 year ago money_helper.php 1 year ago notifications_helper.php 1 year ago order_intent_helper.php 1 year ago orders_helper.php 1 year 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 1 year ago router_helper.php 1 year ago service_helper.php 1 year ago sessions_helper.php 1 year ago settings_helper.php 1 year ago shortcodes_helper.php 1 year ago sms_helper.php 1 year ago steps_helper.php 1 year ago stripe_connect_helper.php 1 year ago styles_helper.php 1 year ago support_topics_helper.php 1 year ago time_helper.php 1 year ago timeline_helper.php 1 year ago transaction_helper.php 1 year ago transaction_intent_helper.php 1 year ago util_helper.php 1 year ago version_specific_updates_helper.php 1 year 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
566 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" 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 <?php echo $auto_search ? 'is-searching' : '' ; ?>" 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( mb_substr($weekdays[ $index ], 0, 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_settings_value( 'earliest_possible_booking', false );
191 $month_settings['latest_possible_booking'] = OsSettingsHelper::get_settings_value( 'latest_possible_booking', false );
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( 'm' ) == $target_date->format( 'm' ) );
326 $is_next_month = ( $day_date->format( 'm' ) > $target_date->format( 'm' ) );
327 $is_prev_month = ( $day_date->format( 'm' ) < $target_date->format( 'm' ) );
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 foreach ( $daily_resources[ $day_date->format( 'Y-m-d' ) ] as $resource ) {
340 if ( $is_day_in_past && $not_in_allowed_period ) {
341 continue;
342 }
343 $work_minutes = array_merge( $work_minutes, $resource->work_minutes );
344 }
345 $work_minutes = array_unique( $work_minutes, SORT_NUMERIC );
346 sort( $work_minutes, SORT_NUMERIC );
347
348
349 $work_boundaries = OsResourceHelper::get_work_boundaries_for_resources( $daily_resources[ $day_date->format( 'Y-m-d' ) ] );
350 $total_work_minutes = $work_boundaries->end_time - $work_boundaries->start_time;
351
352 $booking_slots = OsResourceHelper::get_ordered_booking_slots_from_resources( $daily_resources[ $day_date->format( 'Y-m-d' ) ] );
353
354 $bookable_minutes = [];
355 foreach ( $booking_slots as $booking_slot ) {
356 if ( $booking_slot->can_accomodate( $booking_request->total_attendees ) ) {
357 $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();
358 }
359 }
360 ksort( $bookable_minutes );
361 $bookable_minutes_with_capacity_data = '';
362 // this is a group service
363 if ( $service->is_group_service() && ! $settings['hide_slot_availability_count'] ) {
364 foreach ( $bookable_minutes as $minute => $available_capacity ) {
365 $bookable_minutes_with_capacity_data .= $minute . ':' . $available_capacity . ',';
366 }
367 } else {
368 foreach ( $bookable_minutes as $minute => $available_capacity ) {
369 $bookable_minutes_with_capacity_data .= $minute . ',';
370 }
371 }
372 $bookable_minutes_with_capacity_data = rtrim( $bookable_minutes_with_capacity_data, ',' );
373
374
375 $bookable_slots_count = count( $bookable_minutes );
376 // TODO use work minutes instead to calculate minimum gap
377 $minimum_slot_gap = \LatePoint\Misc\BookingSlot::find_minimum_gap_between_slots( $booking_slots );
378 $day_class = 'os-day os-day-current week-day-' . strtolower( $day_date->format( 'N' ) );
379 $tabbable = true;
380 if ( empty( $bookable_minutes ) ) {
381 $day_class .= ' os-not-available';
382 $tabbable = false;
383 }
384 if ( $is_today ) {
385 $day_class .= ' os-today';
386 }
387 if ( $is_day_in_past ) {
388 $day_class .= ' os-day-passed';
389 $tabbable = false;
390 }
391 if ( $is_target_month ) {
392 $day_class .= ' os-month-current';
393 }
394 if ( $is_next_month ) {
395 $day_class .= ' os-month-next';
396 }
397 if ( $is_prev_month ) {
398 $day_class .= ' os-month-prev';
399 }
400 if ( $not_in_allowed_period ) {
401 $day_class .= ' os-not-in-allowed-period';
402 $tabbable = false;
403 }
404 if ( count( $bookable_minutes ) == 1 && OsStepsHelper::hide_timepicker_when_one_slot_available() ) {
405 $day_class .= ' os-one-slot-only';
406 }
407 if ( ( $day_date->format( 'Y-m-d' ) == $target_date->format( 'Y-m-d' ) ) && $settings['highlight_target_date'] ) {
408 $day_class .= ' selected';
409 }
410 ?>
411
412 <div <?php if($tabbable) echo 'tabindex="0"'; ?> role="button" class="<?php echo esc_attr( $day_class ); ?>"
413 data-date="<?php echo esc_attr( $day_date->format( 'Y-m-d' ) ); ?>"
414 data-nice-date="<?php echo esc_attr( OsTimeHelper::get_nice_date_with_optional_year( $day_date->format( 'Y-m-d' ), false ) ); ?>"
415 data-service-duration="<?php echo esc_attr( $booking_request->duration ); ?>"
416 data-total-work-minutes="<?php echo esc_attr( $total_work_minutes ); ?>"
417 data-work-start-time="<?php echo esc_attr( $work_boundaries->start_time ); ?>"
418 data-work-end-time="<?php echo esc_attr( $work_boundaries->end_time ); ?>"
419 data-bookable-minutes="<?php echo esc_attr( $bookable_minutes_with_capacity_data ); ?>"
420 data-work-minutes="<?php echo esc_attr( implode( ',', $work_minutes ) ); ?>"
421 data-interval="<?php echo esc_attr( $selectable_time_interval ); ?>">
422 <?php if ( $settings['layout'] == 'horizontal' ) { ?>
423 <div
424 class="os-day-weekday"><?php echo esc_html( OsBookingHelper::get_weekday_name_by_number( $day_date->format( 'N' ) ) ); ?></div><?php } ?>
425 <div class="os-day-box">
426 <?php
427 if ( $bookable_slots_count && ! $settings['hide_slot_availability_count'] ) {
428 // translators: %d is the number of slots available
429 echo '<div class="os-available-slots-tooltip">' . esc_html( sprintf( __( '%d Available', 'latepoint' ), $bookable_slots_count ) ) . '</div>';
430 } ?>
431 <div class="os-day-number"><?php echo esc_html( $day_date->format( 'j' ) ); ?></div>
432 <?php if ( ! $is_day_in_past && ! $not_in_allowed_period ) { ?>
433 <div class="os-day-status">
434 <?php
435 if ( $total_work_minutes > 0 && $bookable_slots_count ) {
436 $available_blocks_count = 0;
437 $not_available_started_count = 0;
438 $duration = $booking_request->duration;
439 $end_time = $work_boundaries->end_time - $duration;
440 $processed_count = 0;
441 $last_available_slot_time = false;
442 $bookable_ranges = [];
443 $loop_availability_status = false;
444 for ( $i = 0; $i < count( $booking_slots ); $i ++ ) {
445 if ( $booking_slots[ $i ]->can_accomodate( $booking_request->total_attendees ) ) {
446 // AVAILABLE SLOT
447 if ( $loop_availability_status && $i > 0 && ( ( $booking_slots[ $i ]->start_time - $booking_slots[ $i - 1 ]->start_time ) > $minimum_slot_gap ) ) {
448 // big gap between previous slot and this slot
449 $bookable_ranges[] = $booking_slots[ $i - 1 ]->start_time + $minimum_slot_gap;
450 $bookable_ranges[] = $booking_slots[ $i ]->start_time;
451 }
452 if ( ! $loop_availability_status ) {
453 $bookable_ranges[] = $booking_slots[ $i ]->start_time;
454 }
455 $last_available_slot_time = $booking_slots[ $i ]->start_time;
456 $loop_availability_status = true;
457 } else {
458 // NOT AVAILABLE
459 // 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
460 if ( isset( $booking_slots[ $i + 1 ] ) && $booking_slots[ $i + 1 ]->start_time == $booking_slots[ $i ]->start_time ) {
461 continue;
462 }
463 // 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
464 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 ) {
465 continue;
466 }
467 // if last available slot exists and previous slot was also available
468 if ( $last_available_slot_time && $loop_availability_status ) {
469 $bookable_ranges[] = $last_available_slot_time + $minimum_slot_gap;
470 }
471 $loop_availability_status = false;
472 }
473 }
474 if ( $bookable_ranges ) {
475 for ( $i = 0; $i < count( $bookable_ranges ); $i += 2 ) {
476 $left = ( $bookable_ranges[ $i ] - $work_boundaries->start_time ) / $total_work_minutes * 100;
477 $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 );
478 echo '<div class="day-available" style="left:' . esc_attr( $left ) . '%;width:' . esc_attr( $width ) . '%;"></div>';
479 }
480 }
481 }
482 ?>
483 </div>
484 <?php } ?>
485 </div>
486 </div>
487
488 <?php
489
490 // DAYS LOOP END
491 }
492 echo '</div></div>';
493 }
494
495 // Used on holiday/custom schedule generator lightbox
496 public static function generate_monthly_calendar_days_only( $target_date_string = 'today', $highlight_target_date = false, bool $is_active = false ) {
497 $target_date = new OsWpDateTime( $target_date_string );
498 $calendar_start = clone $target_date;
499 $calendar_start->modify( 'first day of this month' );
500 $calendar_end = clone $target_date;
501 $calendar_end->modify( 'last day of this month' );
502
503 $weekday_for_first_day_of_month = $calendar_start->format( 'N' ) - 1;
504 $weekday_for_last_day_of_month = $calendar_end->format( 'N' ) - 1;
505
506
507 if ( $weekday_for_first_day_of_month > 0 ) {
508 $calendar_start->modify( '-' . $weekday_for_first_day_of_month . ' days' );
509 }
510
511 if ( $weekday_for_last_day_of_month < 6 ) {
512 $days_to_add = 6 - $weekday_for_last_day_of_month;
513 if($days_to_add > 0){
514 $calendar_end->modify( '+' . $days_to_add . ' days' );
515 }
516 }
517
518 $active_class = $is_active ? 'active' : '';
519
520 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' ) ) ) . '">';
521 echo '<div class="os-monthly-calendar-days">';
522 for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
523 $is_today = ( $day_date->format( 'Y-m-d' ) == OsTimeHelper::today_date() ) ? true : false;
524 $is_day_in_past = ( $day_date->format( 'Y-m-d' ) < OsTimeHelper::today_date() ) ? true : false;
525 $day_class = 'os-day os-day-current week-day-' . strtolower( $day_date->format( 'N' ) );
526
527 if ( $day_date->format( 'm' ) > $target_date->format( 'm' ) ) {
528 $day_class .= ' os-month-next';
529 }
530 if ( $day_date->format( 'm' ) < $target_date->format( 'm' ) ) {
531 $day_class .= ' os-month-prev';
532 }
533
534 if ( $is_today ) {
535 $day_class .= ' os-today';
536 }
537 if ( $highlight_target_date && ( $day_date->format( 'Y-m-d' ) == $target_date->format( 'Y-m-d' ) ) ) {
538 $day_class .= ' selected';
539 }
540 if ( $is_day_in_past ) {
541 $day_class .= ' os-day-passed';
542 } ?>
543 <div class="<?php echo esc_attr( $day_class ); ?>" data-date="<?php echo esc_attr( $day_date->format( 'Y-m-d' ) ); ?>">
544 <div class="os-day-box">
545 <div class="os-day-number"><?php echo esc_html( $day_date->format( 'j' ) ); ?></div>
546 </div>
547 </div><?php
548 }
549 echo '</div></div>';
550 }
551
552 public static function generate_calendar_quick_actions_link( OsWpDateTime $day_date, array $settings = [] ) : string {
553 $defaults = [
554 'agent_id' => 0,
555 'location_id' => 0,
556 'service_id' => 0,
557 'start_time' => 600
558 ];
559
560 $settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
561
562 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').'"></a>';
563 }
564
565
566 }