[Back] <?php
/*
* Copyright (c) 2022 LatePoint LLC. All rights reserved.
*/
class OsCalendarHelper {
public static function generate_dates_and_times_picker(OsBookingModel $booking, OsWpDateTime $target_date, $auto_search = false, array $calendar_settings = []) : string{
$is_recurring_supported = apply_filters('latepoint_is_feature_recurring_bookings_on', false);
$can_service_be_recurring = $is_recurring_supported && ($booking->service->get_meta_by_key('allow_recurring_bookings') == 'on');
ob_start(); ?>
<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'; ?>">
<div class="os-dates-w" data-time-pick-style="<?php echo esc_attr(OsStepsHelper::get_time_pick_style()); ?>">
<?php if($auto_search){ ?>
<div class="os-calendar-searching-info"><?php echo sprintf(esc_html__( 'Searching %s for available dates', 'latepoint' ), '<span></span>'); ?></div>
<?php } ?>
<div class="os-calendar-while-searching-wrapper">
<?php OsCalendarHelper::generate_calendar_for_datepicker_step( \LatePoint\Misc\BookingRequest::create_from_booking_model( $booking ), $target_date, $calendar_settings ); ?>
</div>
</div>
<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()); ?>">
<div class="times-header">
<div class="th-line"></div>
<div class="times-header-label">
<?php esc_html_e( 'Pick a slot for', 'latepoint' ); ?> <span></span>
<?php do_action( 'latepoint_step_datepicker_appointment_time_header_label', $booking, $calendar_settings ); ?>
</div>
<div class="th-line"></div>
</div>
<div class="os-times-w">
<div class="timeslots"></div>
</div>
</div>
<?php do_action( 'latepoint_dates_and_times_picker_after', $booking, $target_date, $calendar_settings ); ?>
</div>
<?php
$html = ob_get_clean();
return $html;
}
/**
* Get list of statuses which should not appear on calendar
*
* @return array
*/
public static function get_booking_statuses_hidden_from_calendar(): array {
$statuses = explode( ',', OsSettingsHelper::get_settings_value( 'calendar_hidden_statuses', '' ) );
/**
* Get list of statuses which bookings should not appear on calendar
*
* @param {array} $statuses array of status codes that will be hidden from calendar
* @returns {array} The filtered array of status codes
*
* @since 4.7.0
* @hook latepoint_get_booking_statuses_hidden_from_calendar
*
*/
return apply_filters( 'latepoint_get_booking_statuses_hidden_from_calendar', $statuses );
}
/**
* Returns an array of booking status codes to be displayed on calendar
*
* @return {array} The array of statuses
*/
public static function get_booking_statuses_to_display_on_calendar(): array {
$hidden_statuses = self::get_booking_statuses_hidden_from_calendar();
$all_statuses = OsBookingHelper::get_statuses_list();
$eligible_statuses = [];
foreach ( $all_statuses as $status_code => $status_label ) {
if ( ! in_array( $status_code, $hidden_statuses ) ) {
$eligible_statuses[] = $status_code;
}
}
/**
* Returns an array of booking status codes to be displayed on calendar
*
* @param {array} array of statuses
*
* @returns {array} The array of statuses
*
* @since 4.7.0
* @hook latepoint_get_booking_statuses_to_display_on_calendar
*
*/
return apply_filters( 'latepoint_get_booking_statuses_to_display_on_calendar', $eligible_statuses );
}
public static function is_external_calendar_enabled( string $external_calendar_code ): bool {
return OsSettingsHelper::is_on( 'enable_' . $external_calendar_code );
}
public static function get_list_of_external_calendars( $enabled_only = false ) {
$external_calendars = [];
/**
* Returns an array of external calendars
*
* @param {array} array of calendars
* @param {bool} filter to return only calendars that are enabled
*
* @returns {array} The array of external calendars
*
* @since 4.7.0
* @hook latepoint_list_of_external_calendars
*
*/
return apply_filters( 'latepoint_list_of_external_calendars', $external_calendars, $enabled_only );
}
/**
* @param \LatePoint\Misc\BookingRequest $booking_request
* @param DateTime $target_date
* @param array $settings
*
* @return void
*/
public static function generate_calendar_for_datepicker_step( \LatePoint\Misc\BookingRequest $booking_request, DateTime $target_date, array $settings = [] ) {
$defaults = [
'exclude_booking_ids' => [],
'number_of_months_to_preload' => 1,
'timezone_name' => false,
'layout' => 'classic',
'highlight_target_date' => false,
'consider_cart_items' => false,
'output_target_date_in_header' => false
];
$settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
$weekdays = OsBookingHelper::get_weekdays_arr();
$today_date = new OsWpDateTime( 'today' );
?>
<div class="os-current-month-label-w calendar-mobile-controls">
<div class="os-current-month-label">
<div class="current-month">
<?php if ( $settings['highlight_target_date'] && $settings['output_target_date_in_header'] ) {
echo esc_html( OsTimeHelper::get_nice_date_with_optional_year( $target_date->format( 'Y-m-d' ), false ) );
} else {
echo esc_html( OsUtilHelper::get_month_name_by_number( $target_date->format( 'n' ) ) );
} ?>
</div>
<div class="current-year"><?php echo esc_html( $target_date->format( 'Y' ) ); ?></div>
</div>
<div class="os-month-control-buttons-w">
<button type="button" class="os-month-prev-btn" data-route="<?php echo esc_attr( OsRouterHelper::build_route_name( 'steps', 'load_datepicker_month' ) ); ?>">
<i class="latepoint-icon latepoint-icon-arrow-left"></i></button>
<?php if ( $settings['layout'] == 'horizontal' ) {
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>';
} ?>
<button type="button" class="os-month-next-btn" data-route="<?php echo esc_attr( OsRouterHelper::build_route_name( 'steps', 'load_datepicker_month' ) ); ?>">
<i class="latepoint-icon latepoint-icon-arrow-right"></i></button>
</div>
</div>
<?php if ( $settings['layout'] == 'classic' ) { ?>
<div class="os-weekdays">
<?php
$start_of_week = OsSettingsHelper::get_start_of_week();
// Output the divs for each weekday
for ( $i = $start_of_week - 1; $i < $start_of_week - 1 + 7; $i ++ ) {
// Calculate the index within the range of 0-6
$index = $i % 7;
// Output the div for the current weekday
echo '<div class="weekday weekday-' . esc_attr( $index + 1 ) . '">' . esc_html( mb_substr($weekdays[ $index ], 0, 1) ) . '</div>';
}
?>
</div>
<?php } ?>
<div class="os-months">
<?php
$month_settings = [
'active' => true,
'timezone_name' => $settings['timezone_name'],
'highlight_target_date' => $settings['highlight_target_date'],
'exclude_booking_ids' => $settings['exclude_booking_ids'],
'consider_cart_items' => $settings['consider_cart_items']
];
// if it's not from admin - blackout dates that are not available to select due to date restrictions in settings
$month_settings['earliest_possible_booking'] = OsSettingsHelper::get_earliest_possible_booking_restriction($booking_request->service_id);
$month_settings['latest_possible_booking'] = OsSettingsHelper::get_latest_possible_booking_restriction($booking_request->service_id);
OsCalendarHelper::generate_single_month( $booking_request, $target_date, $month_settings );
for ( $i = 1; $i <= $settings['number_of_months_to_preload']; $i ++ ) {
$next_month_target_date = clone $target_date;
$next_month_target_date->modify( 'first day of next month' );
$month_settings['active'] = false;
$month_settings['highlight_target_date'] = false;
OsCalendarHelper::generate_single_month( $booking_request, $next_month_target_date, $month_settings );
}
?>
</div><?php
/**
* Fired after a datepicker calendar months are generated
*
* @param {BookingRequest} $booking_request instance of a booking request
* @param {DateTime} $target_date target date that is being loaded
* @param {array} $settings array of settings for the calendar
*
* @since 5.1.7
* @hook latepoint_after_datepicker_months
*
*/
do_action('latepoint_after_datepicker_months', $booking_request, $target_date, $settings);
}
public static function generate_single_month( \LatePoint\Misc\BookingRequest $booking_request, DateTime $target_date, array $settings = [] ) {
$defaults = [
'accessed_from_backend' => false,
'active' => false,
'layout' => 'classic',
'highlight_target_date' => false,
'timezone_name' => false,
'earliest_possible_booking' => false,
'latest_possible_booking' => false,
'exclude_booking_ids' => [],
'consider_cart_items' => false,
'hide_slot_availability_count' => OsStepsHelper::hide_slot_availability_count()
];
$settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
// set service to the first available if not set
// IMPORTANT, we have to have service in the booking request, otherwise we can't know duration and intervals
$service = new OsServiceModel();
$service = $service->where( [ 'id' => $booking_request->service_id ] )->set_limit( 1 )->get_results_as_models();
if ( $service ) {
if ( ! $booking_request->duration ) {
$booking_request->duration = $service->duration;
}
$selectable_time_interval = $service->get_timeblock_interval();
} else {
echo '<div class="latepoint-message latepoint-message-error">' . esc_html__( 'In order to generate the calendar, a service must be selected.', 'latepoint' ) . '</div>';
return;
}
# Get bounds for a month of a targetted day
$calendar_start = clone $target_date;
$calendar_start->modify( 'first day of this month' );
$calendar_end = clone $target_date;
$calendar_end->modify( 'last day of this month' );
// 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
if ( $settings['layout'] == 'classic' ) {
$weekday_for_first_day_of_month = intval( $calendar_start->format( 'N' ) );
$weekday_for_last_day_of_month = intval( $calendar_end->format( 'N' ) );
$week_starts_on = OsSettingsHelper::get_start_of_week();
$week_ends_on = $week_starts_on > 1 ? $week_starts_on - 1 : 7;
if ( $weekday_for_first_day_of_month != $week_starts_on ) {
$days_to_subtract = ( $weekday_for_first_day_of_month - $week_starts_on + 7 ) % 7;
if($days_to_subtract > 0){
$calendar_start->modify( '-' . $days_to_subtract . ' days' );
}
}
if ( $weekday_for_last_day_of_month != $week_ends_on ) {
$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 );
if($days_to_add > 0){
$calendar_end->modify( '+' . $days_to_add . ' days' );
}
}
}
$now_datetime = OsTimeHelper::now_datetime_object();
// figure out when the earliest and latest bookings can be placed
try{
$earliest_possible_booking = ( $settings['earliest_possible_booking'] ) ? new OsWpDateTime( $settings['earliest_possible_booking'] ) : clone $now_datetime;
$latest_possible_booking = ( $settings['latest_possible_booking'] ) ? new OsWpDateTime( $settings['latest_possible_booking'] ) : clone $calendar_end;
}catch(Exception $e){
}
// make sure they are set correctly
if ( empty($earliest_possible_booking) ) {
$earliest_possible_booking = clone $now_datetime;
}
if ( empty($latest_possible_booking) ) {
$latest_possible_booking = clone $calendar_end;
}
$date_range_start = ( $calendar_start->format( 'Y-m-d' ) > $earliest_possible_booking->format( 'Y-m-d' ) ) ? clone $calendar_start : clone $earliest_possible_booking;
$date_range_end = ( $calendar_end->format( 'Y-m-d' ) < $latest_possible_booking->format( 'Y-m-d' ) ) ? clone $calendar_end : clone $latest_possible_booking;
// make sure date range is within the requested calendar range
if ( ( $date_range_start->format( 'Y-m-d' ) >= $calendar_start->format( 'Y-m-d' ) )
&& ( $date_range_end->format( 'Y-m-d' ) <= $calendar_end->format( 'Y-m-d' ) )
&& ( $date_range_start->format( 'Y-m-d' ) <= $date_range_end->format( 'Y-m-d' ) ) ) {
$daily_resources = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $date_range_start, $date_range_end, [
'accessed_from_backend' => $settings['accessed_from_backend'],
'exclude_booking_ids' => $settings['exclude_booking_ids'],
'consider_cart_items' => $settings['consider_cart_items'],
'timezone_name' => $settings['timezone_name'],
] );
} else {
$daily_resources = [];
}
$active_class = $settings['active'] ? 'active' : '';
$hide_single_slot_class = OsStepsHelper::hide_timepicker_when_one_slot_available() ? 'hide-if-single-slot' : '';
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' ) ) ) . '">';
echo '<div class="os-monthly-calendar-days">';
// DAYS LOOP START
for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
if ( ! isset( $daily_resources[ $day_date->format( 'Y-m-d' ) ] ) ) {
$daily_resources[ $day_date->format( 'Y-m-d' ) ] = [];
}
$is_today = ( $day_date->format( 'Y-m-d' ) == $now_datetime->format( 'Y-m-d' ) );
$is_day_in_past = ( $day_date->format( 'Y-m-d' ) < $now_datetime->format( 'Y-m-d' ) );
$is_target_month = ( $day_date->format( 'Ym' ) == $target_date->format( 'Ym' ) );
$is_next_month = ( $day_date->format( 'Ym' ) > $target_date->format( 'Ym' ) );
$is_prev_month = ( $day_date->format( 'Ym' ) < $target_date->format( 'Ym' ) );
$not_in_allowed_period = false;
if ( $day_date->format( 'Y-m-d' ) < $earliest_possible_booking->format( 'Y-m-d' ) ) {
$not_in_allowed_period = true;
}
if ( $day_date->format( 'Y-m-d' ) > $latest_possible_booking->format( 'Y-m-d' ) ) {
$not_in_allowed_period = true;
}
$work_minutes = [];
if($settings['accessed_from_backend'] || !$not_in_allowed_period ){
// only do this if is in allowed period or accessed from backend
foreach ( $daily_resources[ $day_date->format( 'Y-m-d' ) ] as $resource ) {
if ( $is_day_in_past && $not_in_allowed_period ) {
continue;
}
$work_minutes = array_merge( $work_minutes, $resource->work_minutes );
}
$work_minutes = array_unique( $work_minutes, SORT_NUMERIC );
sort( $work_minutes, SORT_NUMERIC );
}
$work_boundaries = OsResourceHelper::get_work_boundaries_for_resources( $daily_resources[ $day_date->format( 'Y-m-d' ) ] );
$total_work_minutes = $work_boundaries->end_time - $work_boundaries->start_time;
$booking_slots = OsResourceHelper::get_ordered_booking_slots_from_resources( $daily_resources[ $day_date->format( 'Y-m-d' ) ] );
$bookable_minutes = [];
if($settings['accessed_from_backend'] || !$not_in_allowed_period ) {
// only do this if is in allowed period or accessed from backend
foreach ( $booking_slots as $booking_slot ) {
if ( $booking_slot->can_accomodate( $booking_request->total_attendees ) ) {
$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();
}
}
ksort( $bookable_minutes );
}
$bookable_minutes_with_capacity_data = '';
// this is a group service
if ( $service->is_group_service() && ! $settings['hide_slot_availability_count'] ) {
foreach ( $bookable_minutes as $minute => $available_capacity ) {
$bookable_minutes_with_capacity_data .= $minute . ':' . $available_capacity . ',';
}
} else {
foreach ( $bookable_minutes as $minute => $available_capacity ) {
$bookable_minutes_with_capacity_data .= $minute . ',';
}
}
$bookable_minutes_with_capacity_data = rtrim( $bookable_minutes_with_capacity_data, ',' );
$bookable_slots_count = count( $bookable_minutes );
// TODO use work minutes instead to calculate minimum gap
$minimum_slot_gap = \LatePoint\Misc\BookingSlot::find_minimum_gap_between_slots( $booking_slots );
$day_class = 'os-day os-day-current week-day-' . strtolower( $day_date->format( 'N' ) );
$tabbable = true;
if ( empty( $bookable_minutes ) ) {
$day_class .= ' os-not-available';
$tabbable = false;
}
if ( $is_today ) {
$day_class .= ' os-today';
}
if ( $is_day_in_past ) {
$day_class .= ' os-day-passed';
$tabbable = false;
}
if ( $is_target_month ) {
$day_class .= ' os-month-current';
}
if ( $is_next_month ) {
$day_class .= ' os-month-next';
}
if ( $is_prev_month ) {
$day_class .= ' os-month-prev';
}
if ( $not_in_allowed_period ) {
$day_class .= ' os-not-in-allowed-period';
$tabbable = false;
}
if ( count( $bookable_minutes ) == 1 && OsStepsHelper::hide_timepicker_when_one_slot_available() ) {
$day_class .= ' os-one-slot-only';
}
if ( ( $day_date->format( 'Y-m-d' ) == $target_date->format( 'Y-m-d' ) ) && $settings['highlight_target_date'] ) {
$day_class .= ' selected';
}
?>
<div <?php if($tabbable) echo 'tabindex="0"'; ?> role="button" class="<?php echo esc_attr( $day_class ); ?>"
data-date="<?php echo esc_attr( $day_date->format( 'Y-m-d' ) ); ?>"
data-nice-date="<?php echo esc_attr( OsTimeHelper::get_nice_date_with_optional_year( $day_date->format( 'Y-m-d' ), false ) ); ?>"
data-service-duration="<?php echo esc_attr( $booking_request->duration ); ?>"
data-total-work-minutes="<?php echo esc_attr( $total_work_minutes ); ?>"
data-work-start-time="<?php echo esc_attr( $work_boundaries->start_time ); ?>"
data-work-end-time="<?php echo esc_attr( $work_boundaries->end_time ); ?>"
data-bookable-minutes="<?php echo esc_attr( $bookable_minutes_with_capacity_data ); ?>"
data-work-minutes="<?php echo esc_attr( implode( ',', $work_minutes ) ); ?>"
data-interval="<?php echo esc_attr( $selectable_time_interval ); ?>">
<?php if ( $settings['layout'] == 'horizontal' ) { ?>
<div
class="os-day-weekday"><?php echo substr(esc_html( OsBookingHelper::get_weekday_name_by_number( $day_date->format( 'N' ) ) ), 0, 1); ?></div><?php } ?>
<div class="os-day-box">
<?php
if ( $bookable_slots_count && ! $settings['hide_slot_availability_count'] ) {
// translators: %d is the number of slots available
echo '<div class="os-available-slots-tooltip">' . esc_html( sprintf( __( '%d Available', 'latepoint' ), $bookable_slots_count ) ) . '</div>';
} ?>
<div class="os-day-number"><?php echo esc_html( $day_date->format( 'j' ) ); ?></div>
<?php if ( ! $is_day_in_past && ! $not_in_allowed_period ) { ?>
<div class="os-day-status">
<?php
if ( $total_work_minutes > 0 && $bookable_slots_count ) {
$available_blocks_count = 0;
$not_available_started_count = 0;
$duration = $booking_request->duration;
$end_time = $work_boundaries->end_time - $duration;
$processed_count = 0;
$last_available_slot_time = false;
$bookable_ranges = [];
$loop_availability_status = false;
for ( $i = 0; $i < count( $booking_slots ); $i ++ ) {
if ( $booking_slots[ $i ]->can_accomodate( $booking_request->total_attendees ) ) {
// AVAILABLE SLOT
if ( $loop_availability_status && $i > 0 && ( ( $booking_slots[ $i ]->start_time - $booking_slots[ $i - 1 ]->start_time ) > $minimum_slot_gap ) ) {
// big gap between previous slot and this slot
$bookable_ranges[] = $booking_slots[ $i - 1 ]->start_time + $minimum_slot_gap;
$bookable_ranges[] = $booking_slots[ $i ]->start_time;
}
if ( ! $loop_availability_status ) {
$bookable_ranges[] = $booking_slots[ $i ]->start_time;
}
$last_available_slot_time = $booking_slots[ $i ]->start_time;
$loop_availability_status = true;
} else {
// NOT AVAILABLE
// 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
if ( isset( $booking_slots[ $i + 1 ] ) && $booking_slots[ $i + 1 ]->start_time == $booking_slots[ $i ]->start_time ) {
continue;
}
// 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
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 ) {
continue;
}
// if last available slot exists and previous slot was also available
if ( $last_available_slot_time && $loop_availability_status ) {
$bookable_ranges[] = $last_available_slot_time + $minimum_slot_gap;
}
$loop_availability_status = false;
}
}
if ( $bookable_ranges ) {
for ( $i = 0; $i < count( $bookable_ranges ); $i += 2 ) {
$left = ( $bookable_ranges[ $i ] - $work_boundaries->start_time ) / $total_work_minutes * 100;
$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 );
echo '<div class="day-available" style="left:' . esc_attr( $left ) . '%;width:' . esc_attr( $width ) . '%;"></div>';
}
}
}
?>
</div>
<?php } ?>
</div>
</div>
<?php
// DAYS LOOP END
}
echo '</div></div>';
}
// Used on holiday/custom schedule generator lightbox
public static function generate_monthly_calendar_days_only( $target_date_string = 'today', $highlight_target_date = false, bool $is_active = false ) {
$target_date = new OsWpDateTime( $target_date_string );
$calendar_start = clone $target_date;
$calendar_start->modify( 'first day of this month' );
$calendar_end = clone $target_date;
$calendar_end->modify( 'last day of this month' );
$weekday_for_first_day_of_month = $calendar_start->format( 'N' ) - 1;
$weekday_for_last_day_of_month = $calendar_end->format( 'N' ) - 1;
if ( $weekday_for_first_day_of_month > 0 ) {
$calendar_start->modify( '-' . $weekday_for_first_day_of_month . ' days' );
}
if ( $weekday_for_last_day_of_month < 6 ) {
$days_to_add = 6 - $weekday_for_last_day_of_month;
if($days_to_add > 0){
$calendar_end->modify( '+' . $days_to_add . ' days' );
}
}
$active_class = $is_active ? 'active' : '';
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' ) ) ) . '">';
echo '<div class="os-monthly-calendar-days">';
for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
$is_today = ( $day_date->format( 'Y-m-d' ) == OsTimeHelper::today_date() ) ? true : false;
$is_day_in_past = ( $day_date->format( 'Y-m-d' ) < OsTimeHelper::today_date() ) ? true : false;
$day_class = 'os-day os-day-current week-day-' . strtolower( $day_date->format( 'N' ) );
if ( $day_date->format( 'm' ) > $target_date->format( 'm' ) ) {
$day_class .= ' os-month-next';
}
if ( $day_date->format( 'm' ) < $target_date->format( 'm' ) ) {
$day_class .= ' os-month-prev';
}
if ( $is_today ) {
$day_class .= ' os-today';
}
if ( $highlight_target_date && ( $day_date->format( 'Y-m-d' ) == $target_date->format( 'Y-m-d' ) ) ) {
$day_class .= ' selected';
}
if ( $is_day_in_past ) {
$day_class .= ' os-day-passed';
} ?>
<div class="<?php echo esc_attr( $day_class ); ?>" data-date="<?php echo esc_attr( $day_date->format( 'Y-m-d' ) ); ?>">
<div class="os-day-box">
<div class="os-day-number"><?php echo esc_html( $day_date->format( 'j' ) ); ?></div>
</div>
</div><?php
}
echo '</div></div>';
}
public static function generate_calendar_quick_actions_link( OsWpDateTime $day_date, array $settings = [] ) : string {
$defaults = [
'agent_id' => 0,
'location_id' => 0,
'service_id' => 0,
'start_time' => 600
];
$settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
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>';
}
}