2026-03-26 12:07:55 +00:00

701 lines
18 KiB
PHP

<?php
/**
* Elementor Upcoming Appointments Widget.
*
* Elementor widget that inserts a coverflow-style carousel into the page.
*
* @since 1.0.0
*/
class SSA_Elementor_Booking_Widget extends \Elementor\Widget_Base {
/**
* Get widget name.
*
* Retrieve coverflow carousel widget name.
*
* @since 1.0.0
* @access public
*
* @return string Widget name.
*/
public function get_name() {
return 'ssa-booking';
}
/**
* Get widget title.
*
* Retrieve Upcoming Appointments widget title.
*
* @since 1.0.0
* @access public
*
* @return string Widget title.
*/
public function get_title() {
return __( 'Schedule an Appointment', 'simply-schedule-appointments' );
}
/**
* Get widget icon.
*
* Retrieve Upcoming Appointments widget icon.
*
* @since 1.0.0
* @access public
*
* @return string Widget icon.
*/
public function get_icon() {
return 'eicon-calendar';
}
/**
* Get widget keywords.
*
* Retrieve the list of keywords the widget belongs to.
*
* @since 2.1.0
* @access public
*
* @return array Widget keywords.
*/
public function get_keywords() {
return [ 'ssa', 'schedule', 'calendar', 'appointments', 'simply', 'booking' ];
}
/**
* Get widget categories.
*
* Retrieve the list of categories the Upcoming Appointments widget belongs to.
*
* @since 1.0.0
* @access public
*
* @return array Widget categories.
*/
public function get_categories() {
return [ 'general' ];
}
/**
* Retrieve the list of scripts the Upcoming Appointments widget depended on.
*
* Used to set scripts dependencies required to run the widget.
*
* @since 1.3.0
* @access public
*
* @return array Widget scripts dependencies.
*/
public function get_script_depends() {
return [];
}
/**
* Retrieve the list of styles the Upcoming Appointments widget depended on.
*
* Used to set style dependencies required to run the widget.
*
* @since 1.3.0
* @access public
*
* @return array Widget styles dependencies.
*/
public function get_style_depends() {
return [];
}
/**
* Register Upcoming Appointments widget controls.
*
* Adds different input fields to allow the user to change and customize the widget settings.
*
* @since 1.0.0
* @access protected
*/
protected function register_controls() {
$this->start_controls_section(
'block_section',
[
'label' => __( 'Block Description', 'simply-schedule-appointments' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
$icon_url = $this->get_icon();
$this->add_control(
'title',
[
'raw' => '<div><i class="' . esc_attr($icon_url) . '"></i><span> Appointment Booking Form</span></div>',
'type' => \Elementor\Controls_Manager::RAW_HTML,
]
);
$this->add_control(
'description',
[
'raw' => '<p>' .esc_html__('Displays an Appointment Booking Form. You can customize the appointment type and styles.', 'simply-schedule-appointments') . '</p>',
'type' => \Elementor\Controls_Manager::RAW_HTML,
]
);
$this->end_controls_section();
$this->start_controls_section(
'section_ssa_booking',
[
'label' => __( 'Select Appointment Types', 'simply-schedule-appointments' ),
]
);
$appointment_type_labels = ssa()->appointment_type_label_model->query();
$ssa_appointment_label_key_values = wp_list_pluck( $appointment_type_labels, 'name', 'id' );
$ssa_appointment_label_key_values[''] = 'All';
$ssa_appointment_label_key_values = array_reverse( $ssa_appointment_label_key_values, true );
$appointment_types = ssa()->appointment_type_model->query( array(
'number' => -1,
'status' => 'publish',
) );
$filter_options = [
'types' => __( 'Appointment types', 'simply-schedule-appointments' ),
'label' => __( 'Label', 'simply-schedule-appointments' ),
];
$default_filter = key($filter_options);
$map_appointment_type_ids_to_labels = wp_list_pluck( $appointment_types, 'title', 'slug' );
$map_appointment_type_ids_to_labels = array_reverse( $map_appointment_type_ids_to_labels, true );
$map_appointment_type_ids_to_labels[''] = 'All';
$map_appointment_type_ids_to_labels = array_reverse( $map_appointment_type_ids_to_labels, true );
$this->add_control(
'appointment_types_filter',
[
'label' => __( 'FILTER BY', 'simply-schedule-appointments' ),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => $default_filter,
'options' => $filter_options,
'classes' => 'ssa-booking-block',
]
);
$this->add_control(
'label',
[
'label' => __( 'Labels', 'simply-schedule-appointments' ),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => '',
'options' => $ssa_appointment_label_key_values,
'condition' => [
'appointment_types_filter' => 'label',
],
]
);
$this->add_control(
'header',
[
'raw' => '<h4>Select Appointment Types:</h4>',
'type' => \Elementor\Controls_Manager::RAW_HTML,
'separator' => 'after',
'condition' => [
'appointment_types' => 'types',
],
]
);
$this->add_control(
'appointment_type',
[
'label' => esc_html__('Appointment Types', 'simply-schedule-appointments'),
'type' => \Elementor\Controls_Manager::SELECT2,
'options' => $map_appointment_type_ids_to_labels,
'label_block' => true,
'multiple' => true,
'frontend_available' => true,
'condition' => [
'appointment_types_filter' => 'types',
],
]
);
$this->end_controls_section();
/* Appointment type view */
$this->start_controls_section(
'section_ssa_booking_type_view',
[
'label' => __( 'Appointment Types View', 'simply-schedule-appointments' ),
]
);
$filter_types_options = [
'cardList' => __('List', 'simply-schedule-appointments'),
'cardGrid' => __('Grid', 'simply-schedule-appointments'),
'cardColumns' => __('Two Columns', 'simply-schedule-appointments'),
];
$default_filter_type = key($filter_types_options);
$this->add_control(
'appointment_types_view',
[
'label' => __( 'Appointments types view', 'simply-schedule-appointments' ),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => $default_filter_type,
'options' => $filter_types_options,
'label_block' => true,
'classes' => 'ssa-booking-block',
]
);
$this->end_controls_section();
/* Booking Flow UI*/
if ( ssa_should_render_booking_flow()) {
$this->start_controls_section(
'section_ssa_booking_flow',
[
'label' => __( 'Booking Flow', 'simply-schedule-appointments' ),
]
);
$filter_flow_options = [
'appt_type_settings' => __('Use default settings from appointment type', 'simply-schedule-appointments'),
'expanded' => __('Expanded', 'simply-schedule-appointments'),
'express' => __('Express', 'simply-schedule-appointments'),
'first_available' => __('First available', 'simply-schedule-appointments'),
];
$default_flow_filter = key($filter_flow_options);
$this->add_control(
'booking_layout',
[
'label' => esc_html__( 'Main Booking Layout', 'simply-schedule-appointments' ),
'label_block' => true,
'frontend_available' => true,
'type' => \Elementor\Controls_Manager::SELECT,
'default' => $default_flow_filter,
'options' => $filter_flow_options,
]
);
$this->add_control(
'appt_duration',
[
'label' => esc_html__( 'Duration', 'simply-schedule-appointments' ),
'label_block' => false,
'type' => \Elementor\Controls_Manager::NUMBER,
'min' => 1,
'default' => 1,
'condition' => [
'booking_layout' => 'first_available',
]
]
);
$filter_duration_units = [
'minutes' => __('Minutes', 'simply-schedule-appointments'),
'hours' => __('Hours', 'simply-schedule-appointments'),
'days' => __('Days', 'simply-schedule-appointments'),
'weeks' => __('Weeks', 'simply-schedule-appointments'),
];
$default_duration_unit = key($filter_duration_units);
$this->add_control(
'duration_unit',
[
'label' => esc_html__( 'Duration Unit', 'simply-schedule-appointments' ),
'label_block' => true,
'type' => \Elementor\Controls_Manager::SELECT,
'default' => $default_duration_unit,
'options' => $filter_duration_units,
'condition' => [
'booking_layout' => 'first_available',
],
]
);
$filter_fallback_options = [
'expanded' => __('Expanded', 'simply-schedule-appointments'),
'express' => __('Express', 'simply-schedule-appointments'),
];
$default_fallback_filter = key($filter_fallback_options);
$this->add_control(
'fallback_options',
[
'label' => esc_html__( 'Fallback Flow', 'simply-schedule-appointments' ),
'label_block' => true,
'type' => \Elementor\Controls_Manager::SELECT,
'default' => $default_fallback_filter,
'options' => $filter_fallback_options,
'condition' => [
'booking_layout' => 'first_available',
],
]
);
$this->add_control(
'view_type',
[
'label' => esc_html__( 'View Type', 'simply-schedule-appointments' ),
'type' => \Elementor\Controls_Manager::CHOOSE,
'frontend_available' => true,
'options' => [
'left' => [
'title' => esc_html__( 'Date View', 'simply-schedule-appointments' ),
'icon' => 'eicon-calendar',
],
'right' => [
'title' => esc_html__( 'Time View', 'simply-schedule-appointments' ),
'icon' => 'eicon-clock-o',
],
],
'default' => 'left',
'toggle' => true,
'selectors_dictionary' => [
'left' => is_rtl() ? 'end' : 'start',
'right' => is_rtl() ? 'start' : 'end',
],
'conditions' => [
'relation' => 'and',
'terms' => [
[
'name' => 'booking_layout',
'operator' => '!==',
'value' => 'express',
],
[
'name' => 'booking_layout',
'operator' => '!==',
'value' => 'appt_type_settings',
],
]
],
]
);
$filter_date_options = [
'week' => __('Weekly', 'simply-schedule-appointments'),
'month' => __('Monthly', 'simply-schedule-appointments'),
'only_available' =>__('Only available dates', 'simply-schedule-appointments'),
];
$default_date_filter = key($filter_date_options);
$this->add_control(
'date_view_options',
[
'label' => esc_html__( 'Date View', 'simply-schedule-appointments' ),
'label_block' => true,
'type' => \Elementor\Controls_Manager::SELECT,
'default' => $default_date_filter,
'options' => $filter_date_options,
'conditions' => [
'relation' => 'and',
'terms' => [
[
'name' => 'view_type',
'operator' => '===',
'value' => 'left',
],
[
'relation' => 'and',
'terms' => [
[
'name' => 'booking_layout',
'operator' => '!==',
'value' => 'express',
],
[
'name' => 'booking_layout',
'operator' => '!==',
'value' => 'appt_type_settings',
],
],
],
],
],
]
);
$filter_time_options = [
'time_of_day_columns' => __('Time of day columns', 'simply-schedule-appointments'),
'single_column' => __('Single column', 'simply-schedule-appointments'),
];
$default_time_filter = key($filter_time_options);
$this->add_control(
'time_view_options',
[
'label' => esc_html__( 'Time View', 'simply-schedule-appointments' ),
'label_block' => true,
'type' => \Elementor\Controls_Manager::SELECT,
'default' => $default_time_filter,
'options' => $filter_time_options,
'conditions' => [
'relation' => 'or',
'terms' => [
[
'name' => 'booking_layout',
'operator' => '===',
'value' => 'express',
],
[
'relation' => 'and',
'terms' => [
[
'name' => 'view_type',
'operator' => '===',
'value' => 'right',
],
[
'name' => 'booking_layout',
'operator' => '!==',
'value' => 'appt_type_settings',
],
],
]
]
],
]
);
$this->end_controls_section();
}
$this->start_controls_section(
'section_style',
[
'label' => __( 'Styles', 'elementor' ),
'tab' => \Elementor\Controls_Manager::TAB_STYLE,
]
);
$this->add_control(
'accent_color',
[
'label' => __( 'Accent Color', 'simply-schedule-appointments' ),
'type' => \Elementor\Controls_Manager::COLOR,
'alpha' => false,
]
);
$this->add_control(
'background_color',
[
'label' => __( 'Background Color', 'simply-schedule-appointments' ),
'type' => \Elementor\Controls_Manager::COLOR,
'alpha' => false,
]
);
$this->add_control(
'font_family',
[
'label' => __( 'Font Family', 'simply-schedule-appointments' ),
'type' => \Elementor\Controls_Manager::FONT,
]
);
$this->add_control(
'padding',
[
'label' => __( 'Padding', 'simply-schedule-appointments' ),
'type' => \Elementor\Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'vw', '%' ],
'range' => [
'px' => [
'min' => 0,
'max' => 100,
'step' => 1
],
'em' => [
'min' => 0,
'max' => 100,
'step' => 1
],
'rem' => [
'min' => 0,
'max' => 100,
'step' => 1
],
'vw' => [
'min' => 0,
'max' => 100,
'step' => 1
],
'%' => [
'min' => 0,
'max' => 100,
'step' => 1
],
],
]
);
$this->end_controls_section();
}
public function get_global_color($global_colors, $new_color){
$new_color_id = substr($new_color, strpos($new_color, 'colors?id=')+10);
foreach($global_colors as $global_color){
if($global_color['_id']!= $new_color_id){
continue;
}
$new_color = ltrim($global_color['color'], '#');
}
return $new_color;
}
/**
* Converts time units to minutes
*
* This function converts time units to minutes based on the duration and unit type passed to the function, returning their equivalent.
*
* @param int $duration - The amount of time that needs conversion.
* @param string $unit - The time format (minutes, hours, days, weeks).
*
* @return int|null The amount after conversion, or null if the input is invalid.
*/
function convertDurationToMinutes($duration, $unit) {
// Validate if the duration is an integer
$duration = filter_var($duration, FILTER_VALIDATE_INT);
// If duration is not a valid integer, null, zero, or a negative integer return an error or null
if ($duration === false || $duration <= 0) {
return null;
}
switch ($unit) {
case 'minutes':
return $duration;
case 'hours':
return $duration * 60;
case 'days':
return $duration * 60 * 24;
case 'weeks':
return $duration * 60 * 24 * 7;
default:
return null;
}
}
/**
* Render Upcoming Appointments widget output on the frontend.
*
* Written in PHP and used to generate the final HTML.
*
* @since 1.0.0
* @access protected
*/
protected function render() {
$settings = $this->get_settings_for_display();
if ( $settings['appointment_type'] === 'none' || $settings['appointment_type'] === 'all' ) {
$settings['appointment_type'] = '';
}
$attrs = array();
$kit = Elementor\Plugin::$instance->kits_manager->get_active_kit_for_frontend();
$system_colors = $kit->get_settings_for_display( 'system_colors' );
$custom_colors = $kit->get_settings_for_display( 'custom_colors' );
$global_colors = array_merge( $system_colors, $custom_colors );
if( $settings['appointment_type'] && $settings['appointment_type'] !== '') {
if( is_array($settings['appointment_type'])){
$attrs['types'] = implode(',', $settings['appointment_type']);
}else {
$attrs['types'] = $settings['appointment_type'];
$settings['appointment_type'] = '';
}
}
if( $settings['label'] && $settings['label'] !== '') {
$attrs['label'] = $settings['label'];
}
if ( $settings['appointment_types_view'] && $settings['appointment_types_view'] !== '' ) {
$attrs['appointment_types_view'] = $settings['appointment_types_view'];
}
if ( ssa_should_render_booking_flow() ) {
if ( $settings['booking_layout'] !== 'appt_type_settings' && !empty( $settings['booking_layout'] ) ) {
if ( $settings['booking_layout'] === 'expanded' ) {
$attrs['flow'] = $settings['booking_layout'];
$attrs['date_view'] = $settings['date_view_options'];
$attrs['time_view'] = $settings['time_view_options'];
}
else if ( $settings['booking_layout'] === 'express' ) {
$attrs['flow'] = $settings['booking_layout'];
$attrs['time_view'] = $settings['time_view_options'];
}
else if ( $settings['booking_layout'] === 'first_available' ) {
$attrs['flow'] = $settings['booking_layout'];
$attrs['suggest_first_available'] = true;
// Only available within
$attrs['suggest_first_available_within_minutes'] = $this->convertDurationToMinutes( $settings['appt_duration'], $settings['duration_unit'] );
// Fallback flow
if ( $settings['fallback_options'] === 'expanded' ) {
$attrs['fallback_flow'] = $settings['fallback_options'];
$attrs['date_view'] = $settings['date_view_options'];
$attrs['time_view'] = $settings['time_view_options'];
}
else if ( $settings['fallback_options'] === 'express' ) {
$attrs['fallback_flow'] = $settings['fallback_options'];
$attrs['time_view'] = $settings['time_view_options'];
}
}
}
}
if( $settings['accent_color'] && $settings['accent_color'] !== '' ) {
$attrs['accent_color'] = ltrim( $settings['accent_color'], '#');
} else if ( ! empty( $settings['__globals__']['accent_color'] ) ) {
$attrs['accent_color'] = $this->get_global_color($global_colors, $settings['__globals__']['accent_color']);
}
if( $settings['background_color'] && $settings['background_color'] !== '' ) {
$attrs['background'] = ltrim( $settings['background_color'], '#' );
} else if (!empty($settings['__globals__']['background_color'])) {
$attrs['background'] = $this->get_global_color($global_colors, $settings['__globals__']['background_color']);
}
if( $settings['font_family'] && $settings['font_family'] !== '' ) {
$attrs['font'] = $settings['font_family'];
}
if( $settings['padding'] && $settings['padding'] !== '' ) {
$attrs['padding'] = $settings['padding']['size'] . $settings['padding']['unit'];
}
?>
<div class="elementor-ssa-booking-wrapper">
<div <?php echo $this->get_render_attribute_string( 'booking' ); ?>>
<div class="ssa-booking">
<?php echo ssa()->shortcodes->ssa_booking( $attrs ); ?>
</div>
</div>
</div>
<?php
}
}