Setting booking time programmatically

Hello,
i need to set booking start and end hours with a code snippet, so that no matter what the user sets in the time multi select, the booking time will always set to 2023-10-14 from 10:00 to 15:00.

I tried this code snippet but doesn’t seem to work.



// Add the filter to alter the booking make form
add_filter('hivepress/v1/forms/booking_make', 'custom_alter_booking_make_form', 10, 2);

function custom_alter_booking_make_form($form_args, $form) {
    
    // Check if booking and listing exist
    if ($form->get_model() && $form->get_model()->get_listing()) {
        // Set your custom start and end time
        $start_time = strtotime('2023-10-14 10:00:00'); // Example: October 14, 2023, 10:00 AM
        $end_time = strtotime('2023-10-17 15:00:00');   // Example: October 14, 2023, 3:00 PM

        // Modify the start and end time fields in the form_args array
        $form_args['fields']['_time']['options'] = array(
            date('H:i', $start_time) => date('H:i', $start_time),
            date('H:i', $end_time) => date('H:i', $end_time),
        );

    }

    return $form_args;
}

Any idea?
Thank you in advance!

Hi,

Sorry, there’s no simple code snippet for this, it would require a custom implementation. If customizations beyond the available features are required for your site, please consider hiring someone for custom work: https://fvrr.co/32e7LvY

Hi Andrii,
thank you for your reply.
I’m a developer so I can do the work by myself, but is not clear how can i change the booking start and end time with filters and actions.
There are no documentation about the hooks for the booking plugins so i need to ask you.
I just need to know what hook do I have to call and what filed I have to change in order to set the time like the multi select does.
Thank you in advance.

Hi,
I found a way with a simple code snippet:

add_action('hivepress/v1/models/booking/update', function($booking_id ) {
    
	if(hivepress()->get_version( 'bookings' )){

        update_post_meta( $booking_id, 'hp_start_time',  strtotime('2023-10-14 10:00'));
        update_post_meta( $booking_id, 'hp_end_time', strtotime('2023-10-14 15:00'));     
	}
});

This will simply update the time in the database after the booking creation.

2 Likes

hi, is it also possible to let customers pick a custom start and end time ? so there are two time inputs for the customer.

Hi,
I’m working exactly on this.
As soon as i will figure out the whole system i will try to explain it to you!

hey, really appreciate that
thank you!

Hi,
So here is the process, hope you will understand, if not fell free to ask.

  1. (optional)
    Action hook to set default 30min slot intervals on all booking listings
// SET DEFOULT 30min BOOKING SLOT INTERVAL
add_action('hivepress/v1/models/listing/create', function($listing_id) {
	if(hivepress()->get_version( 'bookings' )){
		update_post_meta($listing_id, 'hp_booking_slot_duration', 30);
		update_post_meta($listing_id, 'hp_booking_min_time', 3600);
        

	}
});
  1. (optional)
    Deactivate the fields to set the time spans
add_filter(
	'hivepress/v1/models/listing/attributes',
	function( $attributes ) {
		if ( isset($attributes['booking_slot_duration']) ) {
			$attributes['booking_slot_duration']['editable'] = false;
		}
		
		if ( isset($attributes['booking_slot_interval']) ) {
			$attributes['booking_slot_interval']['editable'] = false;
		}
		
		if ( isset($attributes['booking_moderated']) ) {
			$attributes['booking_moderated']['editable'] = false;
		}

		return $attributes;
	},
	1000,
    2
);
  1. Add two select fields for the time start and time end (same as the time select of hivepress)

// ADD START AND END TIME IN BOOKING FORM
add_filter('hivepress/v1/forms/booking_make', 'form_mod1', 1000);
function form_mod1( $form ) {

    $form['fields']['_start_time'] = [
        'label'      => esc_html__( 'Start Time', 'hivepress-bookings' ),
        'type'       => 'select',
        'options'    => [],
        'source'     =>  $form['fields']['_time']['source'],
        'required'   => true,
        '_separate'  => true,
        '_order'     => 5,

        'attributes' => [
            'data-parent' => '_dates',
            'class' => [ 'booking-time-start' ],
        ],
    ];

    $form['fields']['_end_time'] = [
        'label'      => esc_html__( 'End Time', 'hivepress-bookings' ),
        'type'       => 'select',
        'options'    => [],
        'source'     =>  $form['fields']['_time']['source'],
        'required'   => false,
        '_separate'  => true,
        '_order'     => 5,

        'attributes' => [
            'data-parent' => '_dates',
            'class' => [ 'booking-time-end' ],
        ],
    ];

    // $form['fields']['_end_time'] = $form['fields']['_time']
			
    return $form;
}
  1. Add css snippers to the new fields

#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(3){
    display: inline-block;
}

#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(4){
    float: right;
}

.hp-form__field--select{
    display: block;
}
  1. Add javascript code to handle:
  • vendor minimum service time, so if min. serv. time of that listing is 2:30h and the working time is 9:00 - 20:00 on start time you will only be able to select up to 17:30 so you can order 2:30h booking.
  • end time will change options based on start value selected, so if user selects start time 16:00 the end time will have only available selects from 18:30.
  • edit send booking button, so it will prevent default action and it will manually change the link to the make-booking page with costum start and and and date params

NOTE: here I hardcoded “2:30” min. serv. time (see “getMinimumServiceTime” function), in future i should get this parameter from listing attributes. If you dont need min. serv. time put this “0:00”.


// HANDE BOOKING TIME ORDER HOURS
add_action('wp_footer', 'booking_time_handler');

function booking_time_handler()
{
    ?>
    <script>
        requestButtonClick();

        // waitForElm("remove_dash_li", "body > span > span > span.select2-results", "");
        // waitForElm("remove_dash_span", "#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(3) > span > span.selection > span", "");
        var all_li_elements = [];

        waitForElm("handle_span_change", getStartSpanOuterSelector());
        waitForElm("remove_li_after_maximum", getUlOuterSelector());
        
        function waitForElm(operation, selector, costum_values) {
            return new Promise(resolve => {
                if (document.querySelector(selector)) {
                    return resolve(document.querySelector(selector));
                }

            const observer = new MutationObserver(mutations => {
                if (document.querySelector(selector)) {

                    resolve(document.querySelector(selector));

                    switch (operation) {
                        case "remove_dash_li":
                            // remove_li_after_dash(selector, observer);
                            break;

                        case "remove_dash_span":
                            // remove_span_after_dash(selector, observer, time_remove_executions);
                            break;
                    
                        case "handle_span_change":
                            handle_span_change(observer);
                            update_multi_time_select(observer);
                            addBtnClick();

                            break;

                        case "remove_li_before_minimum":
                            remove_li_before_minimum(selector, observer);
                            break;
                            
                        case "remove_li_after_maximum":
                            remove_li_after_maximum(selector, observer);
                            break;                           
                    
                        default:
                            break;
                    }
                }
            });

            observer_connect(observer);
        });


        function observer_connect(observer){

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }

        // function remove_li_after_dash(selector, observer){

        //     var ul_el = document.querySelector(selector).getElementsByTagName("ul")[0];        

        //     if(!ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')
        //         && !ul_el.classList.contains('already_edited')){
                
                    
        //         ul_el.classList.add("already_edited");
        //         observer.disconnect();  

        //         var li_elements = ul_el.getElementsByTagName("li");

        //         for (let i = 0; i < li_elements.length; i++) {
        //             var li_text = li_elements[i].innerHTML;

        //             li_elements[i].innerHTML = li_text.split("-")[0];


        //         }

        //         observer_connect(observer);
        //     }

        // }

        // function remove_span_after_dash(selector, observer, time_remove_executions){
        //     var span_outer_el = document.querySelector(selector);
        //     var span_inner_el = span_outer_el.getElementsByTagName("span")[0];    
        //     span_inner_el.get

        //     if(!span_inner_el.innerHTML.includes("—")){
                    
        //             observer.disconnect();  

        //             span_text = span_inner_el.innerHTML;
        //             span_inner_el.innerHTML = span_text.split("-")[0];

        //             time_remove_executions++;

        //             if(time_remove_executions < 2){

        //                 observer_connect(observer);
        //             }
        //         }
            
        // }

        function handle_span_change(observer){
            // START TIME SPAN
            var span_inner_el_start  = getStartSpanInner();

            // END TIME SPAN
            var span_inner_el_end = getEndSpanInner();
            

            if(span_inner_el_start.innerHTML != "—"){

                var minimum_end = getMinimumEnd();

                update_end_time_if_less_then_min();

                waitForElm("remove_li_before_minimum", getUlOuterSelector());
            }
            else if(span_inner_el_start.innerHTML == "—"
                    &&  span_inner_el_end.innerHTML != "—"){
                
                span_inner_el_start.innerHTML = "—";
                span_inner_el_end.innerHTML = "—";
            }


        }


        function remove_li_before_minimum(selector, observer){

            var ul_el = getUlInner() 


            if(!ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                
                observer.disconnect();  

                var li_elements = ul_el.getElementsByTagName("li");

                // remove only if is end ul
                if(!is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;

                        
                        end_ul_set_selected();

                        // when minimum end time is reached, stop removing li elements
                        if(compareTimes(li_text, ">=", getMinimumEnd())){
                            break;
                        }
                        else{
                            ul_el.removeChild(li_elements[i]);
                            i--;
                        }
                        // li_elements[i].innerHTML = li_text.split("-")[0];
                        
                    }
                    
                }
                observer_connect(observer);
            }
        }

        function remove_li_after_maximum(selector, observer){

            var ul_el = getUlInner() 

            if(!ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                
                var li_elements = ul_el.getElementsByTagName("li");
                var maximum_end = getMaximumEnd();

                // save all li elements with also removed ones (save before removing)
                if(all_li_elements.length == 0){
                    all_li_elements = li_elements;
                }

                observer.disconnect();  


                // remove only if is end ul
                if(is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;
                        
                        // when macimum end time is reached, remove all next li elements
                        if(compareTimes(li_text, ">", maximum_end)){
                            ul_el.removeChild(li_elements[i]);
                            i--;
                        }
                    }
                    
                }
                observer_connect(observer);
            }
        }
        

        function is_start_ul(li_elements){
            for (let i = 0; i < li_elements.length; i++) {
                
                // get start time from start span
                var start_time =  getCurrentStartValue();

                // if no time is set, say is start ul also if is not
                if(start_time == "—")
                    return true;

                // if the li is equal to the start selected time is the start ul
                if(li_elements[i].innerHTML == start_time
                    && li_elements[i].getAttribute("aria-selected") == "true"){

                    return true;
                }
            }

            // otherwise is end ul
            return false;
        }

        function compareTimes(time1, operator, time2){
            // Extract hours and minutes from time strings
            const [hours1, minutes1] = time1.split(':').map(Number);
            const [hours2, minutes2] = time2.split(':').map(Number);

            // Calculate total minutes since midnight for each time
            const totalMinutes1 = hours1 * 60 + minutes1;
            const totalMinutes2 = hours2 * 60 + minutes2;

            switch (operator) {
                case ">":
                    return totalMinutes1 > totalMinutes2;
                    break;

                case "<":
                    return totalMinutes1 < totalMinutes2;
                    break;

                case ">=":
                    return totalMinutes1 >= totalMinutes2;
                    break;
            
                case "<=":
                    return totalMinutes1 <= totalMinutes2;
                    break;

                case "==":
                    return totalMinutes1 == totalMinutes2;
                    break;
                    
                default:
                    return false;
                    break;
            }
        }

        function update_end_time_if_less_then_min(){
            var start_span_inner = getStartSpanInner();
            var end_span_inner = getEndSpanInner();

            var minimumEnd = getMinimumEnd();


            if(end_span_inner.innerHTML == "—"
                || compareTimes(end_span_inner.innerHTML, "<", minimumEnd)){

                    end_span_inner.innerHTML = minimumEnd;
                }

        }

        function end_ul_set_selected(){

            var selector = "body > span > span > span.select2-results";
            var ul_el = getUlInner();


            if(!ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                

                var li_elements = ul_el.getElementsByTagName("li");

                // remove only if is end ul
                if(!is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;
                        
                        // when minimum end time is reached, stop removing li elements
                        if(compareTimes(li_text, "==", getCurrentEndValue())){
                            li_elements[i].setAttribute("aria-selected", "true");
                        }
                        else{
                            li_elements[i].setAttribute("aria-selected", "false");
                        }
                    }
                }
            }               
        }

        function update_multi_time_select(observer){
            // if start and end are selected
            if(getCurrentStartValue() != "—" 
                && getCurrentEndValue() != "—"){
                    
                    observer.disconnect();

                    var multi_select_ul = getMultiSelectUl();
                    // var insert_li_template = '<li class="select2-selection__choice" title="08:00" data-select2-id="145"><span class="select2-selection__choice__remove" role="presentation">×</span>08:00</li>';

                    // multi_select_ul.click();

                    var select_text_input = document.querySelector("#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(5) > span > span.selection > span > ul > li > input");

                    // Create a new keyboard event
                    var event = new Event("input", {
                        bubbles: true,
                        cancelable: true,
                    });

                    // Set the value of the input field
                    select_text_input.value = "a";

                    // Dispatch the keyboard event
                    select_text_input.dispatchEvent(event);

                    // var multi_select_ul_outer = getMultiSelectUlOuter();
                    // var insert_li_template_outer = '<option value="32400" data-select2-id="52">09:00</option>';

                    // multi_select_ul.innerHTML = insert_li_template + multi_select_ul.innerHTML;
                    // multi_select_ul_outer.innerHTML = insert_li_template_outer + multi_select_ul_outer.innerHTML;

                    var ul_el = getUlInner();

                    if(ul_el){
                        var li_elements = ul_el.getElementsByTagName("li");
                        
                        console.log(" FOUND "+li_elements.length);

                        performActionWhenArrayHasMoreThanOneElement(li_elements);
                    }
                    else{
                        console.log("NOT FOUND");

                    }

                    // observer_connect(observer);
                }

                function waitForMultipleElements(arr, minimumElements) {
                    return new Promise(resolve => {
                        const checkArrayLength = () => {
                        if (arr.length >= minimumElements) {
                            resolve();
                        } else {
                            setTimeout(checkArrayLength, 100); // Check again after 100 milliseconds
                        }
                        };

                        checkArrayLength(); // Start the checking process
                    });
                }

                async function performActionWhenArrayHasMoreThanOneElement(arr) {
                    await waitForMultipleElements(arr, 2); // Wait for at least 2 elements in the array
                    // Perform your action here

                    for (let i = 0; i < li_elements.length; i++) {
                            console.log(li_elements[i].innerHTML);
                            
                            if(i == 3){
                                li_elements[i].click();
                                console.log("CLICKED");
                                li_elements[i].style.backgroundColor = "red";
                            }
                        }
                }

        }

        function addBtnClick(){

            var button =document.querySelector('#content > div > div > div > div > aside > div > div > form > div.hp-form__footer > button');


            if(!button.hasClickListener){
                button.addEventListener('click', function() {
                    // Code to be executed when the button is clicked
                    button.hasClickListener = true;

                    var form = document.querySelector("#content > div > div > div > div > aside > div > div > form");

                    form.addEventListener("submit", function(event) {
                        console.log("SUBMIT");

                        event.preventDefault(); // Prevent the form from submitting automatically

                        var actionLink = form.getAttribute("action");
                        var listing_id = 
                            <?php
                                echo $GLOBALS["listing_id"];
                            ?>

                        var start_time =getCurrentStartValue();
                        var end_time =getCurrentEndValue();
                        var date = getCurrentDateValue();

                        var updatedActionLink = actionLink + "?listing="+listing_id+"&start_time="+start_time+"&end_time="+end_time+"&date="+date;

                        // Manually redirect to the updated URL
                        window.location.href = updatedActionLink;
                    });

                });
            }
        }
    }

    function getCurrentStartValue(){
        var start_span_inner = document.querySelector(getStartSpanOuterSelector()).getElementsByTagName("span")[0];
        return start_span_inner.innerHTML;
    }

    function getCurrentEndValue(){
        var end_span_inner = getEndSpanInner();
        return end_span_inner.innerHTML;
    }

    function getCurrentDateValue(){
        var date_input = document.querySelector("#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div.hp-form__field.hp-form__field--date > div > input.hp-field.hp-field--text.flatpickr-input");

        return date_input.value;
    }
        
    function getMinimumServiceTime(){
        return "2:30";                      // -> GET FROM PARAMS WORDPRESS
    }

    function getUlOuterSelector(){
        return "body > span > span > span.select2-results";                      
    }

    function getStartSpanOuterSelector(){
        return "#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(3) > span > span.selection > span";                      
    }

    function getEndSpanOuterSelector(){
        return "#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(4) > span > span.selection > span";                      
    }

    function getUlInner(){
        return document.querySelector(getUlOuterSelector()).getElementsByTagName("ul")[0];
    }

    function getMultiSelectUl(){
        return document.querySelector("#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(5) > span > span.selection > span > ul");
    }

    function getMultiSelectUlOuter(){
        return document.querySelector("#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(5) > select");
    }

    function getStartSpanInner(){
        return document.querySelector(getStartSpanOuterSelector()).getElementsByTagName("span")[0];
    }

    function getEndSpanInner(){
        return document.querySelector(getEndSpanOuterSelector()).getElementsByTagName("span")[0];
    }

    function getMinimumEnd(){

        var start_time = getCurrentStartValue();
        var minimum_service_time = getMinimumServiceTime();     

        // Parse the start_time string to create a Date object
        var startTimeParts = start_time.split(":");
        var startDate = new Date();
        startDate.setHours(parseInt(startTimeParts[0], 10));
        startDate.setMinutes(parseInt(startTimeParts[1], 10));

        // Parse the minimum_service_time string to create a Date object
        var [minHours, minMinutes] = minimum_service_time.split(':');
        minHours = parseInt(minHours);
        minMinutes = parseInt(minMinutes);

        // Add 2 hours and 30 minutes to the start_time
        var endTime = new Date(startDate.getTime() + minHours * 60 * 60 * 1000 + minMinutes * 60 * 1000);

        // Format the endTime as "HH:mm"
        var endHours = endTime.getHours().toString().padStart(2, '0');
        var endMinutes = endTime.getMinutes().toString().padStart(2, '0');
        var min_end = endHours + ":" + endMinutes;

        return min_end;
    }

    function getMaximumEnd(){

        var minimum_service_time = getMinimumServiceTime();     
        
        var ul_el = getUlInner();
        var li_elements = ul_el.getElementsByTagName("li"); 
        var last_hour = li_elements[li_elements.length - 1].innerHTML;

        // Parse the start_time string to create a Date object
        var lastTimeParts = last_hour.split(":");
        var lastDate = new Date();
        lastDate.setHours(parseInt(lastTimeParts[0], 10));
        lastDate.setMinutes(parseInt(lastTimeParts[1], 10));

        // Parse the last_hour string to create a Date object
        var [minHours, minMinutes] = minimum_service_time.split(':');
        minHours = parseInt(minHours);
        minMinutes = parseInt(minMinutes);

        // Add 2 hours and 30 minutes to the last_hour
        var endTime = new Date(lastDate.getTime() - (minHours * 60 * 60 * 1000 + minMinutes * 60 * 1000));

        // Format the endTime as "HH:mm"
        var endHours = endTime.getHours().toString().padStart(2, '0');
        var endMinutes = endTime.getMinutes().toString().padStart(2, '0');
        var max_end = endHours + ":" + endMinutes;

        return max_end;
    }

    function requestButtonClick(){
    }

    </script>
    <?php
}
  1. Save listing id in $GLOBALS php variable to use it after to redirect to the correct listing page (see point 5 third “-”)

// ADD LISTING ID ON GLOBALS
add_filter(
    'hivepress/v1/templates/listing_view_page/blocks',
    function ($blocks, $template) {
        $listing = $template->get_context('listing');

        if (!$listing) {
            return $blocks;
        }

        $listing_id = $listing->get_id();

        // set listing id in globals
        $GLOBALS["listing_id"] = "$listing_id";

        return $blocks;
    },
    1000,
    2
);
  1. Change the start and end time in the database after the booking is created with the “wrong” (default) time. The $_GET parameters were passed in the link url parameters before.

// SET COSTUM DATE AND TIME
add_action('hivepress/v1/models/booking/update', function($booking_id ) {
    
	if(hivepress()->get_version( 'bookings' )){

        $start_time = $_GET['start_time'];
        $end_time = $_GET['end_time'];
        $date = $_GET['date'];

        update_post_meta( $booking_id, 'hp_start_time',  strtotime("$date $start_time"));
        update_post_meta( $booking_id, 'hp_end_time', strtotime("$date $end_time"));     
	}
});

  1. Set date on first place (order) of the form and unset the hivepress time select

add_filter(
	'hivepress/v1/forms/booking_make',
	function( $form ) {

		$form['fields']['_dates']['_order'] = 1;
        
        unset($form['fields']['_time']);

		return $form;
	},
	1000,
    2
);
  1. Add CSS Style snippet to hive “(optional)” in end time (the end time will be automaticly set so there is no way it will be empty)

#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(4) > label > small{
    display: none;
}
  1. To remove the second half of booking time listing slots (after the dash (“-”)), thas is essential for my script, check this topic and follow my technique (not raccomended) or try to make what yevhen explained in the post.

That’s it!
NOTE 1: that as I said before there is only hardcoded minimum service time
NOTE 2: the price calculation is still work in progress so for now it stays as default, if you need i will send you also this when i will finish.
NOTE 3: Im not sure I used the raccomended procedures everywere, I think I made too much hardcoding. If you have some suggestions to make the code better tell me please.

Hope I will help you!

2 Likes

Hey,

really appreciate your work.
Thank you so much!
I will have a deeper look at the code at the weekend.
There are just two problems i found:

1:
The ordering process works, but when the vendor has to accept the order he is seeing this time:


Over the whole order process it is showing: current time - current time (like in the photo)

2:
On the mobile version it is possible to make illogical time sets

But you did a really good job. The ground function is working.

hmu when you fixed the time calculation
you helped me alot :smiley:

Hi Whammer,

thank you, I didn’t notice this problem, you helped me too!
I think i fixed both problems!

For the first problem i think hivepress somewhere overwrites the database field value when confirming the booking. What I did is to overwrite again after the confirmation.
I edited the function on booking update action, so it will save in $GLOBALS the start time, end time and date (only when they are set otherwise it will overwrite with empty value).

Look at the differences:

Copy the code here:

// SET COSTUM DATE AND TIME
add_action('hivepress/v1/models/booking/update', function($booking_id ) {
    
	if(hivepress()->get_version( 'bookings' )
        && isset($_GET['start_time'])){

        $start_time = $_GET['start_time'];
        $end_time = $_GET['end_time'];
        $date = $_GET['date'];

        $GLOBALS['start_time'] = $_GET['start_time'];
        $GLOBALS['end_time'] = $_GET['end_time'];
        $GLOBALS['date'] = $_GET['date'];

        update_post_meta( $booking_id, 'hp_start_time',  strtotime("$date $start_time"));
        update_post_meta( $booking_id, 'hp_end_time', strtotime("$date $end_time"));     
	}
});

Then i added a new action (update_end_time) that is called after the booking confirmation that is writing again the values in the database:

// OVERWRITE TIME AND DATE AGAIN AFTER BOOKING COMPLETE
add_action('hivepress/v1/models/booking/update_end_time', function($booking_id ) {

	if(hivepress()->get_version( 'bookings' )){

        $start_time = $GLOBALS['start_time'];
        $end_time = $GLOBALS['end_time'];
        $date = $GLOBALS['date'];

        update_post_meta( $booking_id, 'hp_start_time',  strtotime("$date $start_time"));
        update_post_meta( $booking_id, 'hp_end_time', strtotime("$date $end_time"));     
	}
});

Then I noticed that sometimes I got an error when requesting a booking and i fixed editing where i get the listing id from globals like this (in “addBtnClick()” function):

Copy here the code:

    var listing_id = 
        <?php
            if(isset($GLOBALS["listing_id"])){
                echo $GLOBALS["listing_id"];
            }else{
                echo "ERR: listing id not found";
            }
        ?>;

For the second problem, the error was because of the selectors, when changing the screen size the selectors weren’t correct because the html elements changed, so the selector also should change.
I this I shouldn’t use selector, because if hivepress adds some blocks with a update the selectors will not be correct again. Tell me if you know a better way to do it.
For now i did like this:

I edited the “getStartSpanOuterSelector()” and put this code:

    function getStartSpanOuterSelector(){
        var selector1 = "#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(3) > span > span.selection > span";                      
        var selector2 = "#content > div > div > div > div > aside > form > div.hp-form__fields > div:nth-child(3) > span > span.selection > span";
        var correct_selector = "";

        if(document.querySelector(selector1)){
            correct_selector = selector1;
        }
        else{
            correct_selector = selector2;
        }

        return correct_selector;
    }

and i also edited the “getEndSpanOuterSelector()” function:

    function getEndSpanOuterSelector(){
        var selector1 = "#content > div > div > div > div > aside > div > div > form > div.hp-form__fields > div:nth-child(4) > span > span.selection > span";
        var selector2 = "#content > div > div > div > div > aside > form > div.hp-form__fields > div:nth-child(4) > span > span.selection > span";
        var correct_selector = "";

        if(document.querySelector(selector1)){
            correct_selector = selector1;
        }
        else{
            correct_selector = selector2;
        }

        return correct_selector;
    }

I also deleted “getMultiSelectUl()” and “getMultiSelectUlOuter()” functions that I didn’t use.

As last thing I edited the style CSS file becouse:

  • “(optional)” on end time was visible on mobile display
  • end time select was placing under start time and not on the side

Here is the new CSS (all you need, the old CSS can be deleted):


.hp-form__field.hp-form__field--select{
    display: inline-block;
}

.hp-form__field.hp-form__field--select:nth-of-type(3) {
    float: right;
}

.hp-form__field--select{
    display: block;
}

.hp-field__label.hp-form__label small{
    display: none;
}

Hope I helped you!
I will let you know for the price calculations ASAP.
( =

Hey,

thank your for the fast solution.
The first problem is solved for desktop version. :smiley:

The solution of the second problem is broking the function for the pc layout.
With this code both (pc and mobile) don’t work well.

But appreciate your help.

Best regards,
Marc

Hi Marc!

After a whole day of work I fixed all the problems (this time I hope for real XD).

Don’t know why when I checked the last think I sent you I didn’t saw any problem, my bad.
The problems where most because of the selectors. When changing the size the selectors changed too so they didn’t get the correct elements.

I fixed by adding on almost every element a custum id so i was able to get the correct elements no matter the screen size.

I made a lot of changes, is too long to explain to you all of them.
I will just give you below all the code snippets you need, if you need to chef the differences you can use a diff checker.

The CSS is the same as the last that I sent you.

Thank you again for letting me know the errors, tell me if this time you are able to find any XD.
Have a nice day!


<?php

// SET DEFOULT 30min BOOKING SLOT INTERVAL
add_action('hivepress/v1/models/listing/create', function($listing_id) {
	if(hivepress()->get_version( 'bookings' )){
		update_post_meta($listing_id, 'hp_booking_slot_duration', 30);
		update_post_meta($listing_id, 'hp_booking_min_time', 3600);
        

	}
});


add_filter(
	'hivepress/v1/models/listing/attributes',
	function( $attributes ) {
		if ( isset($attributes['booking_slot_duration']) ) {
			$attributes['booking_slot_duration']['editable'] = false;
		}
		
		if ( isset($attributes['booking_slot_interval']) ) {
			$attributes['booking_slot_interval']['editable'] = false;
		}
		
		if ( isset($attributes['booking_moderated']) ) {
			$attributes['booking_moderated']['editable'] = false;
		}

		return $attributes;
	},
	1000,
    2
);

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// ADD LISTING ID ON GLOBALS
add_filter(
    'hivepress/v1/templates/listing_view_page/blocks',
    function ($blocks, $template) {
        $listing = $template->get_context('listing');

        if (!$listing) {
            return $blocks;
        }

        $listing_id = $listing->get_id();

        // set listing id in globals
        $GLOBALS["listing_id"] = "$listing_id";

        return $blocks;
    },
    1000,
    2
);

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// ADD START AND END TIME IN BOOKING FORM
add_filter('hivepress/v1/forms/booking_make', 'form_mod1', 1000);
function form_mod1( $form ) {

    $form['fields']['_start_time'] = [
        'label'      => esc_html__( 'Start Time', 'hivepress-bookings' ),
        'type'       => 'select',
        'source'     =>  $form['fields']['_time']['source'],
        'required'   => true,
        '_separate'  => true,
        '_order'     => 5,

        'attributes' => [
            'data-parent' => '_dates',
            'class' => [ 'booking-time-start' ],
            'id' => 'booking-time-start',
        ],
    ];

    $form['fields']['_end_time'] = [
        'label'      => esc_html__( 'End Time', 'hivepress-bookings' ),
        'type'       => 'select',
        'source'     =>  $form['fields']['_time']['source'],
        'required'   => false,
        '_separate'  => true,
        '_order'     => 5,

        'attributes' => [
            'data-parent' => '_dates',
            'class' => [ 'booking-time-end' ],
            'id' => 'booking-time-end',
        ],
    ];

    $form['fields']['_dates']["attributes"]["id"] = "booking-date";

    $form['button']["attributes"]["id"] = "request-booking-btn";
	
    $form["attributes"]["id"] = "booking-form";

    return $form;
}


//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// HANDE BOOKING TIME ORDER HOURS
add_action('wp_footer', 'booking_time_handler');
    
function booking_time_handler()
{
    ?>
    <script>
        var all_li_elements = [];
        
        waitForElm("remove_dash_li", "body > span > span > span.select2-results");
        waitForElm("handle_start_span_change", getStartSpanSelector());
        waitForElm("handle_end_span_change", getEndSpanSelector());
        waitForElm("remove_li_after_maximum", getUlOuterSelector());
        
        function waitForElm(operation, selector, costum_values) {
            return new Promise(resolve => {
                if (document.querySelector(selector)) {
                    return resolve(document.querySelector(selector));
                }

            const observer = new MutationObserver(mutations => {
                if (document.querySelector(selector)) {

                    resolve(document.querySelector(selector));

                    switch (operation) {
                        case "remove_dash_li":
                            remove_li_after_dash(selector, observer);
                            break;
                    
                        case "handle_start_span_change":
                            removeDashFromSpan(selector, observer);
                            handle_span_change(observer);
                            addBtnClick();
                            break;  

                        case "handle_end_span_change":
                            removeDashFromSpan(selector, observer);
                            break; 

                        case "remove_li_before_minimum":
                            remove_li_before_minimum(selector, observer);
                            break;
                            
                        case "remove_li_after_maximum":
                            remove_li_after_maximum(selector, observer);
                            break;     
                               
                            
                    
                        default:
                            break;
                    }
                }
            });

            observer_connect(observer);
        });


        function observer_connect(observer){

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }

        function removeDashFromSpan(selector, observer){
            
            var start_span_el =document.querySelector(selector);
            
            if(start_span_el.innerHTML != "—"
                && start_span_el.innerHTML.includes("-")){
                    start_span_el.innerHTML = start_span_el.innerHTML.split("-")[0];
                }
        }

        function remove_li_after_dash(selector, observer){

            var ul_el = getUlInner();

            if(ul_el.getElementsByTagName("li")[0] &&
                !ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                observer.disconnect();  

                var li_elements = ul_el.getElementsByTagName("li");

                for (let i = 0; i < li_elements.length; i++) {
                    var li_text = li_elements[i].innerHTML;

                    li_elements[i].innerHTML = li_text.split("-")[0];

                }

                observer_connect(observer);
            }
        }

        function remove_span_after_dash(selector, observer, time_remove_executions){
            var span_outer_el = document.querySelector(selector);
            var span_inner_el = span_outer_el.getElementsByTagName("span")[0];    
            span_inner_el.get

            if(!span_inner_el.innerHTML.includes("—")){
                    
                    observer.disconnect();  

                    span_text = span_inner_el.innerHTML;
                    span_inner_el.innerHTML = span_text.split("-")[0];

                    time_remove_executions++;

                    if(time_remove_executions < 2){

                        observer_connect(observer);
                    }
                }
            
        }

        function handle_span_change(observer){
            // START TIME SPAN
            var span_inner_el_start = getStartSpan();

            // END TIME SPAN
            var span_inner_el_end = getEndSpan();
            

            if(span_inner_el_start.innerHTML != "—"){


                var minimum_end = getMinimumEnd();

                update_end_time_if_less_then_min();

                waitForElm("remove_li_before_minimum", getUlOuterSelector());
            }
            else if(span_inner_el_start.innerHTML == "—"
                    &&  span_inner_el_end.innerHTML != "—"){
                
                span_inner_el_start.innerHTML = "—";
                span_inner_el_end.innerHTML = "—";
            }


        }


        function remove_li_before_minimum(selector, observer){

            var ul_el = getUlInner();

            if(ul_el.getElementsByTagName("li")[0]
                && !ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                
                observer.disconnect();  

                var li_elements = ul_el.getElementsByTagName("li");

                // remove only if is end ul
                if(!is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;
                        // li_text = li_text.innerHTML;
                        
                        end_ul_set_selected();

                        // when minimum end time is reached, stop removing li elements
                        if(compareTimes(li_text, ">=", getMinimumEnd())){
                            break;
                        }
                        else{
                            ul_el.removeChild(li_elements[i]);
                            i--;
                        }
                        // li_elements[i].innerHTML = li_text.split("-")[0];
                        
                    }
                    
                }
                observer_connect(observer);
            }
        }

        function remove_li_after_maximum(selector, observer){

            observer.disconnect();  
            var ul_el = getUlInner();

            var li_elements = ul_el.getElementsByTagName("li");

            if(ul_el.getElementsByTagName("li")[0]
                && !ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')
                && !li_elements[li_elements.length - 1].classList.contains("last_li")){
                

                // save all li elements with also removed ones (save before removing)
                if(all_li_elements.length == 0){
                    all_li_elements = li_elements;
                }

                var maximum_end = getMaximumEnd();

                // remove only if is end ul
                if(is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;
                        
                        // when macimum end time is reached, remove all next li elements
                        if(compareTimes(li_text, ">", maximum_end)){
                            ul_el.removeChild(li_elements[i]);
                            i--;
                        }
                    }
                    
                }

                li_elements[li_elements.length - 1].classList.add("last_li");
            }
            observer_connect(observer);
        }
        

        function is_start_ul(li_elements){
            for (let i = 0; i < li_elements.length; i++) {
                
                // get start time from start span
                var start_time =  getCurrentStartValue();

                // if no time is set, say is start ul also if is not
                if(start_time == "—")
                    return true;

                // if the li is equal to the start selected time is the start ul
                if(li_elements[i].innerHTML == start_time
                    && li_elements[i].getAttribute("aria-selected") == "true"){

                    return true;
                }
            }

            // otherwise is end ul
            return false;
        }

        function compareTimes(time1, operator, time2){
            // Extract hours and minutes from time strings
            const [hours1, minutes1] = time1.split(':').map(Number);
            const [hours2, minutes2] = time2.split(':').map(Number);

            // Calculate total minutes since midnight for each time
            const totalMinutes1 = hours1 * 60 + minutes1;
            const totalMinutes2 = hours2 * 60 + minutes2;

            switch (operator) {
                case ">":
                    return totalMinutes1 > totalMinutes2;
                    break;

                case "<":
                    return totalMinutes1 < totalMinutes2;
                    break;

                case ">=":
                    return totalMinutes1 >= totalMinutes2;
                    break;
            
                case "<=":
                    return totalMinutes1 <= totalMinutes2;
                    break;

                case "==":
                    return totalMinutes1 == totalMinutes2;
                    break;
                    
                default:
                    return false;
                    break;
            }
        }

        function update_end_time_if_less_then_min(){
            var start_span_inner = getStartSpan();
            var end_span_inner = getEndSpan();

            var minimumEnd = getMinimumEnd();


            if(end_span_inner.innerHTML == "—"
                || compareTimes(end_span_inner.innerHTML, "<", minimumEnd)){

                    end_span_inner.innerHTML = minimumEnd;
                }

        }

        function end_ul_set_selected(){

            var ul_el = getUlInner();


            if(ul_el.getElementsByTagName("li")[0]
                && !ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                

                var li_elements = ul_el.getElementsByTagName("li");

                // remove only if is end ul
                if(!is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;
                        
                        // when minimum end time is reached, stop removing li elements
                        if(compareTimes(li_text, "==", getCurrentEndValue())){
                            li_elements[i].setAttribute("aria-selected", "true");
                        }
                        else{
                            li_elements[i].setAttribute("aria-selected", "false");
                        }
                    }
                }
            }               
        }

        function addBtnClick(){

            var button = document.querySelector("#request-booking-btn");


            if(!button.hasClickListener){
                button.addEventListener('click', function() {
                    // Code to be executed when the button is clicked
                    button.hasClickListener = true;

                    var form = document.querySelector("#booking-form");

                    form.addEventListener("submit", function(event) {

                        event.preventDefault(); // Prevent the form from submitting automatically

                        var actionLink = form.getAttribute("action");
                        var listing_id = 
                            <?php
                                if(isset($GLOBALS["listing_id"])){
                                    echo $GLOBALS["listing_id"];
                                }else{
                                    echo "ERR: listing id not found";
                                }
                            ?>;

                        var start_time = getCurrentStartValue().trim();
                        var end_time =getCurrentEndValue();
                        var date = getCurrentDateValue();

                        var updatedActionLink = actionLink + "?listing="+listing_id+"&start_time="+start_time+"&end_time="+end_time+"&date="+date;

                        // Manually redirect to the updated URL
                        window.location.href = updatedActionLink;
                    });

                });
            }
        }
    }

    function getCurrentStartValue(){
        var start_span_inner = getStartSpan();
        return start_span_inner.innerHTML;
    }

    function getCurrentEndValue(){
            var end_span_inner = getEndSpan();
            return end_span_inner.innerHTML;
    }

    function getCurrentDateValue(){
        var date_input = document.querySelector("#booking-date").getElementsByTagName("input")[0];

        return date_input.value;
    }
        
    function getMinimumServiceTime(){
        return "2:30";                      // -> GET FROM PARAMS WORDPRESS
    }

    function getUlOuterSelector(){
        return "body > span > span > span.select2-results";                      
    }

    function getStartSpanSelector(){
        return "#select2-booking-time-start-container";
    }

    function getStartSpan(){
        return document.querySelector(getStartSpanSelector());
    }

    function getEndSpanSelector(){
        return "#select2-booking-time-end-container";                      
    }

    function getEndSpan(){
        return document.querySelector(getEndSpanSelector());
    }

    function getUlInner(){
        return document.querySelector(getUlOuterSelector()).getElementsByTagName("ul")[0];
    }



    function getMinimumEnd(){

        var start_time = getCurrentStartValue();
        var minimum_service_time = getMinimumServiceTime();     

        // Parse the start_time string to create a Date object
        var startTimeParts = start_time.split(":");
        var startDate = new Date();
        startDate.setHours(parseInt(startTimeParts[0], 10));
        startDate.setMinutes(parseInt(startTimeParts[1], 10));

        // Parse the minimum_service_time string to create a Date object
        var [minHours, minMinutes] = minimum_service_time.split(':');
        minHours = parseInt(minHours);
        minMinutes = parseInt(minMinutes);

        // Add 2 hours and 30 minutes to the start_time
        var endTime = new Date(startDate.getTime() + minHours * 60 * 60 * 1000 + minMinutes * 60 * 1000);

        // Format the endTime as "HH:mm"
        var endHours = endTime.getHours().toString().padStart(2, '0');
        var endMinutes = endTime.getMinutes().toString().padStart(2, '0');
        var min_end = endHours + ":" + endMinutes;

        return min_end;
    }

    function getMaximumEnd(){

        var minimum_service_time = getMinimumServiceTime();     
        
        var li_elements = all_li_elements; 
        var last_hour = li_elements[li_elements.length - 1].innerHTML;


        // Parse the start_time string to create a Date object
        var lastTimeParts = last_hour.split(":");
        var lastDate = new Date();
        lastDate.setHours(parseInt(lastTimeParts[0], 10));
        lastDate.setMinutes(parseInt(lastTimeParts[1], 10));

        // Parse the last_hour string to create a Date object
        var [minHours, minMinutes] = minimum_service_time.split(':');
        minHours = parseInt(minHours);
        minMinutes = parseInt(minMinutes);

        // Add 2 hours and 30 minutes to the last_hour
        var endTime = new Date(lastDate.getTime() - (minHours * 60 * 60 * 1000 + minMinutes * 60 * 1000));

        // Format the endTime as "HH:mm"
        var endHours = endTime.getHours().toString().padStart(2, '0');
        var endMinutes = endTime.getMinutes().toString().padStart(2, '0');
        var max_end = endHours + ":" + endMinutes;

        return max_end;
    }

    </script>
    <?php
}



// SET COSTUM DATE AND TIME
add_action('hivepress/v1/models/booking/update', function($booking_id ) {
    

    $actual_link = "https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";

	if(hivepress()->get_version( 'bookings' )
        && isset($_GET['start_time'])){

        $start_time = $_GET['start_time'];
        $end_time = $_GET['end_time'];
        $date = $_GET['date'];

        $GLOBALS['start_time'] = $_GET['start_time'];
        $GLOBALS['end_time'] = $_GET['end_time'];
        $GLOBALS['date'] = $_GET['date'];



        update_post_meta( $booking_id, 'hp_start_time',  strtotime("$date $start_time"));
        update_post_meta( $booking_id, 'hp_end_time', strtotime("$date $end_time"));     
	}
});


add_filter(
	'hivepress/v1/forms/booking_make',
	function( $form ) {

		$form['fields']['_dates']['_order'] = 1;
        
        unset($form['fields']['_time']);

		return $form;
	},
	1000,
    2
);

// OVERWRITE TIME AND DATE AGAIN AFTER BOOKING COMPLETE
add_action('hivepress/v1/models/booking/update_end_time', function($booking_id ) {

	if(hivepress()->get_version( 'bookings' )){

        $start_time = $GLOBALS['start_time'];
        $end_time = $GLOBALS['end_time'];
        $date = $GLOBALS['date'];

        update_post_meta( $booking_id, 'hp_start_time',  strtotime("$date $start_time"));
        update_post_meta( $booking_id, 'hp_end_time', strtotime("$date $end_time"));     
	}
});


Note: sometimes I get a “Something went wrong” error when requesting a booking but rarely, let me know if so do you!

EDIT: tell me if you need to make users set tisting minimum service time from params, i added also this feature.

Hey Emilio,
thanks for your endless effort, much appreciate that.
It is working now. :smiley:
Thank you!
I’m getting the errors too, but they also occured sometimes without the custom script.
Seems to be a failure of HivePress self.

You can share it with me, maybe i need that later.
Thank You.

I was just facing one little problem.
If a time period is already booked, it is not available but if i set an earlier start time it is possible in the selection to choose the time after the already booked period as end time.
So double bookings are possible in the time selection.
If u try to book it, you get an error, but maybe it is possible that you can’t select the time, when its already booked.

Sorry for my bad explanation, writing this on my phone.
Maybe have a look at the screenshots:

So in this case the time between 1pm and 3pm is already booked.
But it’s possible to set start time 12pm and end time 5:30 pm.
So the bookings will overlap.

But thanks for your major help.

Best regards,
Marc

Hi whammer,
sorry for my late reply, i was waiting to finish the price logic so I can share it with you.
I was already aware about the problem you explained to me, I will soon fix this and I will share to you the scripts, I already know how to di that.

As I said I finally ended the price calculation logic based on hours. I had to edit lots of think, link the last time here you can copy all the snippets.
Note this works only if you set the time span 30 minutes, with hourly time spans you should probabily change something.


<?php

// SET DEFOULT 30min BOOKING SLOT INTERVAL
add_action('hivepress/v1/models/listing/create', function($listing_id) {
	if(hivepress()->get_version( 'bookings' )){
		update_post_meta($listing_id, 'hp_booking_slot_duration', 30);
		update_post_meta($listing_id, 'hp_booking_min_time', 3600);
        

	}
});


add_filter(
	'hivepress/v1/models/listing/attributes',
	function( $attributes ) {
		if ( isset($attributes['booking_slot_duration']) ) {
			$attributes['booking_slot_duration']['editable'] = false;
		}
		
		if ( isset($attributes['booking_slot_interval']) ) {
			$attributes['booking_slot_interval']['editable'] = false;
		}
		
		if ( isset($attributes['booking_moderated']) ) {
			$attributes['booking_moderated']['editable'] = false;
		}

		return $attributes;
	},
	1000,
    2
);

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// ADD LISTING ID AND MIN. SERV. TIME ON GLOBALS
add_filter(
    'hivepress/v1/templates/listing_view_page/blocks',
    function ($blocks, $template) {
        $listing = $template->get_context('listing');

        if (!$listing) {
            return $blocks;
        }

        $listing_id = $listing->get_id();
        $listing_price = $listing->get_price();

        // set listing id in globals
        $GLOBALS["listing_id"] = $listing_id;
        $GLOBALS["min_serv_time"] = $listing->display_min_service_time();
        $GLOBALS["listing_price"] = $listing_price;

        return $blocks;
    },
    1000,
    2
);

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// ADD START AND END TIME IN BOOKING FORM
add_filter('hivepress/v1/forms/booking_make', 'form_mod1', 1000);
function form_mod1( $form ) {

    $form['fields']['_start_time'] = [
        'label'      => esc_html__( 'Start Time', 'hivepress-bookings' ),
        'type'       => 'select',
        'source'     =>  $form['fields']['_time']['source'],
        'required'   => true,
        '_separate'  => true,
        '_order'     => 5,
        'attributes' => [
            'data-parent' => '_dates',
            'class' => [ 'booking-time-start' ],
            'id' => 'booking-time-start',
            'onchange' => 'start_time_select_change()',
        ],
    ];

    $form['fields']['_end_time'] = [
        'label'      => esc_html__( 'End Time', 'hivepress-bookings' ),
        'type'       => 'select',
        'source'     =>  $form['fields']['_time']['source'],
        'required'   => false,
        '_separate'  => true,
        '_order'     => 5,

        'attributes' => [
            'data-parent' => '_dates',
            'class' => [ 'booking-time-end' ],
            'id' => 'booking-time-end',
            'onchange' => 'end_time_select_change()',
        ],
    ];

    $form['fields']['_dates']["attributes"]["id"] = "booking-date";

    $form['button']["attributes"]["id"] = "request-booking-btn";
	
    $form["attributes"]["id"] = "booking-form";

    return $form;
}


//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// ADD ID TO SIDEBAR PRICE AND TOGGLE PRICE ATOMATIC CHANGE
add_filter(
	'hivepress/v1/templates/listing_view_page',
	function( $template ) {
		return hivepress()->helper->merge_trees(
			$template,
			[
				'blocks' => [
					'page_sidebar' => [
                        'blocks' => [
                                'listing_attributes_primary' => [
								    'area'      => 'view_block_primary',
                                    'attributes' => [
                                        'id' => 'sidebar_listing_price',
                                    ],
                                ],
                        ],
					],
				],
			]
		);
	},
	1000
);

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// DEACTIVARE MULTI TIME SELECT AND SET DATE AS FIRST IN ORDER (in listing booking block form)
add_filter(
	'hivepress/v1/forms/booking_make',
	function( $form ) {

		$form['fields']['_dates']['_order'] = 1;
        
        unset($form['fields']['_time']);

		return $form;
	},
	1000,
    2
);

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// HANDE BOOKING TIME ORDER HOURS
add_action('wp_footer', 'booking_time_handler');
    
function booking_time_handler()
{
    ?>
    <script>
        var all_li_elements = [];
        var current_tot_price = 0;

        waitForElm("remove_dash_li", "body > span > span > span.select2-results");
        waitForElm("handle_start_span_change", getStartSpanSelector());
        waitForElm("handle_end_span_change", getEndSpanSelector());
        waitForElm("remove_li_after_maximum", getUlOuterSelector());
        waitForElm("remove_li_before_minimum", getUlOuterSelector());

        function waitForElm(operation, selector, costum_values) {
            return new Promise(resolve => {
                if (document.querySelector(selector)) {
                    return resolve(document.querySelector(selector));
                }

            const observer = new MutationObserver(mutations => {
                if (document.querySelector(selector)) {

                    resolve(document.querySelector(selector));

                    switch (operation) {
                        case "remove_dash_li":
                            remove_li_after_dash(selector, observer);
                            break;
                    
                        case "handle_start_span_change":
                            removeDashFromSpan(true);
                            addBtnClick();
                            break;  
                            
                        case "handle_end_span_change":
                            removeDashFromSpan(false);
                            break; 

                        case "remove_li_before_minimum":
                            remove_li_before_minimum(selector, observer);
                            break;
                            
                        case "remove_li_after_maximum":
                            remove_li_after_maximum(selector, observer);
                            break;    
                    
                        default:
                            break;
                    }
                }
            });

            observer_connect(observer);
        });
    }

        function observer_connect(observer){

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }

        function removeDashFromSpan(isStartSpan){
            var selector = isStartSpan ? getStartSpanSelector() : getEndSpanSelector();
            
            var span_el = document.querySelector(selector);
            if(span_el.innerHTML != "—"
                && span_el.innerHTML.includes("-")){
                    span_el.innerHTML = span_el.innerHTML.split("-")[0];
                }
        }

        function remove_li_after_dash(selector, observer){

            var ul_el = getUlInner();

            if(ul_el.getElementsByTagName("li")[0] &&
                !ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                observer.disconnect();  

                var li_elements = ul_el.getElementsByTagName("li");

                for (let i = 0; i < li_elements.length; i++) {
                    var li_text = li_elements[i].innerHTML;

                    li_elements[i].innerHTML = li_text.split("-")[0];

                }

                observer_connect(observer);
            }
        }

        function remove_span_after_dash(selector, observer, time_remove_executions){
            var span_outer_el = document.querySelector(selector);
            var span_inner_el = span_outer_el.getElementsByTagName("span")[0];    
            span_inner_el.get

            if(!span_inner_el.innerHTML.includes("—")){
                    
                    observer.disconnect();  

                    span_text = span_inner_el.innerHTML;
                    span_inner_el.innerHTML = span_text.split("-")[0];

                    time_remove_executions++;

                    if(time_remove_executions < 2){

                        observer_connect(observer);
                    }
                }
            
        }

        function handle_span_change(){
            // START TIME SPAN
            var span_inner_el_start = getStartSpan();

            // END TIME SPAN
            var span_inner_el_end = getEndSpan();
            

            if(span_inner_el_start.innerHTML != "—"){


                var minimum_end = getMinimumEnd();

                update_end_time_if_less_then_min();

            }
            else if(span_inner_el_start.innerHTML == "—"
                    &&  span_inner_el_end.innerHTML != "—"){
                
                span_inner_el_start.innerHTML = "—";
                span_inner_el_end.innerHTML = "—";
            }
        }


        function remove_li_before_minimum(selector, observer){

            var ul_el = getUlInner();

            if(ul_el.getElementsByTagName("li")[0]
                && !ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                
                observer.disconnect();  

                var li_elements = ul_el.getElementsByTagName("li");

                // remove only if is end ul
                if(!is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;
                        // li_text = li_text.innerHTML;
                        
                        end_ul_set_selected();

                        // when minimum end time is reached, stop removing li elements
                        if(compareTimes(li_text, ">=", getMinimumEnd())){
                            break;
                        }
                        else{
                            ul_el.removeChild(li_elements[i]);
                            i--;
                        }
                        // li_elements[i].innerHTML = li_text.split("-")[0];
                        
                    }
                    
                }
                observer_connect(observer);
            }
        }

        function remove_li_after_maximum(selector, observer){

            observer.disconnect();  
            var ul_el = getUlInner();

            var li_elements = ul_el.getElementsByTagName("li");

            if(ul_el.getElementsByTagName("li")[0]
                && !ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')
                && !li_elements[li_elements.length - 1].classList.contains("last_li")){
                

                // save all li elements with also removed ones (save before removing)
                if(all_li_elements.length == 0){
                    all_li_elements = li_elements;
                }

                var maximum_end = getMaximumEnd();

                // remove only if is end ul
                if(is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;
                        
                        // when macimum end time is reached, remove all next li elements
                        if(compareTimes(li_text, ">", maximum_end)){
                            ul_el.removeChild(li_elements[i]);
                            i--;
                        }
                    }
                    
                }

                li_elements[li_elements.length - 1].classList.add("last_li");
            }
            observer_connect(observer);
        }
        

        function is_start_ul(li_elements){
            for (let i = 0; i < li_elements.length; i++) {
                
                // get start time from start span
                var start_time =  getCurrentStartValue();

                // if no time is set, say is start ul also if is not
                if(start_time == "—")
                    return true;

                // if the li is equal to the start selected time is the start ul
                if(li_elements[i].innerHTML == start_time
                    && li_elements[i].getAttribute("aria-selected") == "true"){

                    return true;
                }
            }

            // otherwise is end ul
            return false;
        }

        function compareTimes(time1, operator, time2){
            // Extract hours and minutes from time strings
            const [hours1, minutes1] = time1.split(':').map(Number);
            const [hours2, minutes2] = time2.split(':').map(Number);

            // Calculate total minutes since midnight for each time
            const totalMinutes1 = hours1 * 60 + minutes1;
            const totalMinutes2 = hours2 * 60 + minutes2;

            switch (operator) {
                case ">":
                    return totalMinutes1 > totalMinutes2;
                    break;

                case "<":
                    return totalMinutes1 < totalMinutes2;
                    break;

                case ">=":
                    return totalMinutes1 >= totalMinutes2;
                    break;
            
                case "<=":
                    return totalMinutes1 <= totalMinutes2;
                    break;

                case "==":
                    return totalMinutes1 == totalMinutes2;
                    break;
                    
                default:
                    return false;
                    break;
            }
        }

        function update_end_time_if_less_then_min(){
            var start_span_inner = getStartSpan();
            var end_span_inner = getEndSpan();

            var minimumEnd = getMinimumEnd();


            if(end_span_inner.innerHTML == "—"
                || compareTimes(end_span_inner.innerHTML, "<", minimumEnd)){

                    end_span_inner.innerHTML = minimumEnd;
                }

        }

        function end_ul_set_selected(){

            var ul_el = getUlInner();


            if(ul_el.getElementsByTagName("li")[0]
                && !ul_el.getElementsByTagName("li")[0].innerHTML.includes('Searching')){
                

                var li_elements = ul_el.getElementsByTagName("li");

                // remove only if is end ul
                if(!is_start_ul(li_elements)){

                    for (let i = 0; i < li_elements.length; i++) {
                        var li_text = li_elements[i].innerHTML;
                        
                        // when minimum end time is reached, stop removing li elements
                        if(compareTimes(li_text, "==", getCurrentEndValue())){
                            li_elements[i].setAttribute("aria-selected", "true");
                        }
                        else{
                            li_elements[i].setAttribute("aria-selected", "false");
                        }
                    }
                }
            }               
        }

        function addBtnClick(){
            var button = document.querySelector("#request-booking-btn");


            if(!button.hasClickListener){
                button.hasClickListener = true;
                button.addEventListener('click', function() {
                    // Code to be executed when the button is clicked

                    var form = document.querySelector("#booking-form");

                    form.addEventListener("submit", function(event) {

                        event.preventDefault(); // Prevent the form from submitting automatically

                        var actionLink = form.getAttribute("action");
                        var listing_id = 
                            <?php
                                if(isset($GLOBALS["listing_id"])){
                                    echo $GLOBALS["listing_id"];
                                }else{
                                    echo "'NOT_FOUND'";
                                }
                            ?>;

                        if(listing_id != "NOT_FOUND"){

                            var start_time = getCurrentStartValue().trim();
                            var end_time =getCurrentEndValue();
                            var date = getCurrentDateValue();
                            var price = current_tot_price;

                            actionLink += "?listing=" + listing_id;
                            actionLink += "&start_time=" + start_time;
                            actionLink += "&end_time=" + end_time;
                            actionLink += "&date="+date;
                            actionLink += "&price="+price;

                            // Manually redirect to the updated URL
                            
                            window.location.href = actionLink;
                            
                        }

                    });

                });
            }
        }

        function updatePriceVariable(){

            var time1 = getCurrentEndValue();
            var time2 = getCurrentStartValue();
            

            if(!time1.includes("—") && !time2.includes("—")){
                var totHours = getServiceDuration();
                
                var listing_price = <?php
                    if(isset($GLOBALS["listing_price"])){
                        echo $GLOBALS["listing_price"];
                    }else{
                        error_log("ERROR: listing price not found");
                        echo "'NOT_FOUND'";
                    }
                    ?>;
                
                if(listing_price != "NOT_FOUND"){
                    // set commitions (default (none): 1, 0)
                    var commission_percentage = 1.0;          // -> GET FROM HIVEPRESS (values like 1.15 for 15%, 1.2 for 20%)
                    var commission_fee = 0;                 // -> GET FROM HIVEPRESS

                    var finalPrice = listing_price * totHours;
                    // commissions calculation
                    finalPrice = (finalPrice * commission_percentage) + commission_fee;
                    current_tot_price = finalPrice;


                    var priceEl = getPriceElement();
                    priceEl.innerHTML = String(current_tot_price) + " €";
                }
            }
        }

        function start_time_select_change() {

            removeDashFromSpan(true);
            handle_span_change();
            // addBtnClick();
            // observer_connect(observer);
            updatePriceVariable();
            // updatePrice();
        }
        
        function end_time_select_change() {

            // observer.disconnect();
            removeDashFromSpan(false);
            // observer_connect(observer);
            updatePriceVariable();
            // updatePrice();
        }
    
    function getCurrentStartValue(){
        var start_span_inner = getStartSpan();
        return start_span_inner.innerHTML;
    }

    function getCurrentEndValue(){
            var end_span_inner = getEndSpan();
            return end_span_inner.innerHTML;
    }

    function getCurrentDateValue(){
        var date_input = document.querySelector("#booking-date").getElementsByTagName("input")[0];

        return date_input.value;
    }
        
    function getMinimumServiceTime(){

        var min_serv_time = "<?php
            if(isset($GLOBALS["min_serv_time"])){
                echo $GLOBALS["min_serv_time"];
            }
            else{
                echo "ERR: min_serv_time not found";
            }
        ?>";


        return min_serv_time;                      // -> GET FROM PARAMS WORDPRESS
    }

    function getUlOuterSelector(){
        return "body > span > span > span.select2-results";                      
    }

    function getStartSpanSelector(){
        return "#select2-booking-time-start-container";
    }

    function getStartSpan(){
        return document.querySelector(getStartSpanSelector());
    }

    function getEndSpanSelector(){
        return "#select2-booking-time-end-container";                      
    }

    function getEndSpan(){
        return document.querySelector(getEndSpanSelector());
    }

    function getUlInner(){
        return document.querySelector(getUlOuterSelector()).getElementsByTagName("ul")[0];
    }
    
    function getPriceElementSelector(){
        return "#sidebar_listing_price > div";
    }

    function getPriceElement(){
        return document.querySelector(getPriceElementSelector());
    }
        
    


    function getMinimumEnd(){

        var start_time = getCurrentStartValue();
        var minimum_service_time = getMinimumServiceTime();     

        // Parse the start_time string to create a Date object
        var startTimeParts = start_time.split(":");
        var startDate = new Date();
        startDate.setHours(parseInt(startTimeParts[0], 10));
        startDate.setMinutes(parseInt(startTimeParts[1], 10));

        // Parse the minimum_service_time string to create a Date object
        var [minHours, minMinutes] = minimum_service_time.split(':');
        minHours = parseInt(minHours);
        minMinutes = parseInt(minMinutes);

        // Add 2 hours and 30 minutes to the start_time
        var endTime = new Date(startDate.getTime() + minHours * 60 * 60 * 1000 + minMinutes * 60 * 1000);

        // Format the endTime as "HH:mm"
        var endHours = endTime.getHours().toString().padStart(2, '0');
        var endMinutes = endTime.getMinutes().toString().padStart(2, '0');
        var min_end = endHours + ":" + endMinutes;

        return min_end;
    }

    function getMaximumEnd(){

        var minimum_service_time = getMinimumServiceTime();     
        
        var li_elements = all_li_elements; 
        var last_hour = li_elements[li_elements.length - 1].innerHTML;


        // Parse the start_time string to create a Date object
        var lastTimeParts = last_hour.split(":");
        var lastDate = new Date();
        lastDate.setHours(parseInt(lastTimeParts[0], 10));
        lastDate.setMinutes(parseInt(lastTimeParts[1], 10));

        // Parse the last_hour string to create a Date object
        var [minHours, minMinutes] = minimum_service_time.split(':');
        minHours = parseInt(minHours);
        minMinutes = parseInt(minMinutes);

        // Add 2 hours and 30 minutes to the last_hour
        var endTime = new Date(lastDate.getTime() - (minHours * 60 * 60 * 1000 + minMinutes * 60 * 1000));

        // Format the endTime as "HH:mm"
        var endHours = endTime.getHours().toString().padStart(2, '0');
        var endMinutes = endTime.getMinutes().toString().padStart(2, '0');
        var max_end = endHours + ":" + endMinutes;

        return max_end;
    }


    function getServiceDuration(){
        var time1 = getCurrentEndValue();
        var time2 = getCurrentStartValue();
        var result = null;

        if(!time1.includes("—") && !time2.includes("—")){
            const [hours1, minutes1] = time1.split(':').map(Number);
            const [hours2, minutes2] = time2.split(':').map(Number);

            const totalMinutes1 = hours1 * 60 + minutes1;
            const totalMinutes2 = hours2 * 60 + minutes2;

            const differenceInMinutes = totalMinutes1 - totalMinutes2;
            result = differenceInMinutes / 60;
        }

        return result;
    }


    </script>
    <?php
}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// SET COSTUM DATE AND TIME IN THE DATABASE
add_action('hivepress/v1/models/booking/update', function($booking_id ) {

    if(hivepress()->get_version( 'bookings' )
        && isset($_GET['start_time'])){

        $listing_id = $_GET['listing'];
        $start_time = $_GET['start_time'];
        $end_time = $_GET['end_time'];
        $date = $_GET['date'];
        $price = $_GET['price'];

        $GLOBALS['start_time'] = $_GET['start_time'];
        $GLOBALS['end_time'] = $_GET['end_time'];
        $GLOBALS['date'] = $_GET['date'];
        // $GLOBALS['price'] = $_GET['price'];


        update_post_meta( $booking_id, 'hp_start_time',  strtotime("$date $start_time"));
        update_post_meta( $booking_id, 'hp_end_time', strtotime("$date $end_time"));     

        setWooCommerceCartPrice($listing_id, $price);
    }
});

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// Set manually the woocommerce cart price ( *2 the price becouse after we will divide by 2)
function setWooCommerceCartPrice($listing_id, $price){

    // Product ID to add to the cart
    $product_id = getProductId($listing_id);
    
    if($product_id){
        // Get the WooCommerce cart object
        $cart = WC()->cart;

        // Empty cart.
        WC()->cart->empty_cart();

        // Quantity of the product to add to the cart
        $quantity = 1; // You can change this value as needed

        // Add the product to the cart
        $cart->add_to_cart( $product_id, $quantity );

         // Modify cart item prices or quantities here
        foreach (  $cart->get_cart() as $cart_item_key => $cart_item ) {

            // Calculate new price (original price * 3)
            $new_price = $price * 2;

            // Set the new price for the cart item
            $cart_item['data']->set_price( $new_price );

            // Update cart item price and total
            $cart->cart_contents[ $cart_item_key ]['data']->set_price( $new_price );
            $cart->cart_contents[ $cart_item_key ]['line_total'] = $new_price * $cart_item['quantity'];
            $cart->cart_contents[ $cart_item_key ]['line_subtotal'] = $new_price * $cart_item['quantity'];
        }

        // Calculate totals and refresh cart
        $cart->calculate_totals();
    }
}

function getProductId($listing_id){

    $post_name = get_post_field('post_name', $listing_id);

    if ($post_name) {

        $product_id = 0;
        // WP_Query arguments
        $args = array(
            'post_type' => 'product', // Specify the post type
            'name' => $post_name, // Specify the post_name (slug)
            'posts_per_page' => 1, // Limit the number of posts to 1
        );

        // The Query
        $query = new WP_Query($args);

        // Check if the query has any posts
        if ($query->have_posts()) {
            // Loop through the posts
            while ($query->have_posts()) {
                $query->the_post();
                // Get the post ID
                $product_id = get_the_ID();
            }
            // Restore original post data
            wp_reset_postdata();
            return $product_id;
        } else {
            // No posts found
            return null;
        }

    } else {
        return null;
    }
}


//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// every price calculation divide the cart total by 2 so the price will be correct
add_action('woocommerce_before_calculate_totals', 'set_half_price');

function set_half_price($cart) {


    if (is_admin() && !defined('DOING_AJAX')) {
        return;
    }

    foreach ($cart->get_cart() as $cart_item) {
        $product_price = $cart_item['data']->get_price(); // Get the original product price
        $half_price = $product_price / 2; // Calculate half price

        // Set the new price to half only during checkout
        $cart_item['data']->set_price($half_price);
    }
}

Note: here is also the minimum service time logic included, so to make it work you have to add a listing attibute (of type select with values “00:30”, “01:00”, “01:30”,…) with the slug exactly “min_service_time”.
Tell me if you need help to do that.

Let me know if the price logic works well and if you find any issue.

I noticed that the “Something went wrong” problem got worse, I get the errors a cuple of times that I fixed refreshing the page, but after some time refreshing the page is not fixing the error and I had to change the browser to safari where I can fix it with a page refresh.

Let me know if you have any idea how to fix that.
Thanks!

Hey @emilio.micali ,
don’t worry, was also in trouble the whole week.
Thank you again for your solution.
I will test it out on the weekend.

Really appreciate it, when you send me the php snippet for the time logic problem! :smiley:

I’m also receiving “Someting went wrong error” multiple times now, maybe it is fixable with an auto reload page script.
It continues when i reload the page, tested on Chrome and Safari.
Let me know if u find any other fix for that.
I will take research too.

Thanks for all!
Best regards,
Marc

Hi Marc,

I didn’t tried to fix this issue yet, a page-reload script logic could work but would be better to fix the problem at the root.

Anyway I finnally ended the whole time booking logic and it works perfectly!

I needed added two features I needed:

  • different availablity hours for each day of the week (e.g. monday 8 -16, tuesday 15-20…)
  • interval between services chosen from the vendor (if a order is from 12 to 14 and the vendor want a interval between services of 1h and 30 min the nex available booking time will be 15:30 and befor you can order up to 10:30 not more)

I dont know if you need this features but I made lots of changes is difficult for me to sent you the code excluding those logics.

I also fixed the issue about orders overwriting you told me in the 13th message (6 days ago).

Here are the the snippets:

Replace the old snippets i sent you with this:

(the forum didn’t let me send too much characters)

Add this new snippets (for the week days availability):

<?php 
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// ADD ID TO SIDEBAR PRICE AND TOGGLE PRICE ATOMATIC CHANGE
add_filter(
	'hivepress/v1/forms/listing_update',
	function( $form ) {

		return hivepress()->helper->merge_trees(
			$form,
			[
				'fields' => [
					'booking_days' => [
                        'required'   => false,
                        'attributes' => [
                            'id' => 'edit_booking_days',
                            'onchange' => 'days_checkboxes_change(this)',
                        ],
					],
					'booking_min_time' => [
                        'required'   => false,
                        'label' => '',
                        'description' => '',
                        'attributes' => [
                            'id' => 'edit_booking_min_time',
                        ],
					],
					'booking_max_time' => [
                        'required'   => false,
                        'label' => '',
                        'description' => '',
                        'attributes' => [
                            'id' => 'edit_booking_max_time',
                        ],
					],
				],
			]
		);
	},
	1000
);

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

// ADD ID TO SIDEBAR PRICE AND TOGGLE PRICE ATOMATIC CHANGE
add_filter(
	'hivepress/v1/templates/listing_edit_page',
	function( $template ) {

        loadScript();
		return $template;
	},
	1000
);


// ADD ID TO SIDEBAR PRICE AND TOGGLE PRICE ATOMATIC CHANGE
add_filter(
	'hivepress/v1/templates/listing_submit_details_page',
	function( $template ) {

        loadScript();
        setDefaultCheckboxes();
		return $template;
	},
	1000
);



function loadScript(){
?>
<script>
    
    var selectOptionsTemplates = [];
    
window.addEventListener('load', function () {
        
    setSelectOptionsTemplates()
    setOnChangeEvents();

    var checkboxes =document.querySelector('#edit_booking_days');
    days_checkboxes_change(checkboxes);
});

function setSelectOptionsTemplates(){
    for (let i = 1; i < 8; i++) {
        var dayName = getDayOfWeek(i);

        selectOptionsTemplates.push(document.getElementsByName(dayName+"_end")[0].innerHTML);
    }
}

function setOnChangeEvents(){
    for (let i = 1; i < 8; i++) {
        
        document.getElementsByName(getDayOfWeek(i)+"_start")[0].setAttribute("onchange", "startSelectChange(this, "+i+")")
    }
}

function startSelectChange(selectEl, dayNum){
    var selectedOption = selectEl.options[selectEl.selectedIndex].text;

    var corrispondingEndSelect = getCorrispondingEndSelect(selectEl);

    removeOptionsAfter(corrispondingEndSelect, selectedOption, dayNum);
}


function getDayOfWeek(num) {
    var arr = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
    return arr[num-1];
}

function getCorrispondingEndSelect(selectEl){
    var startSelectName = selectEl.name;
    var endSelectName =startSelectName.replace("_start", "_end");

    return document.getElementsByName(endSelectName)[0];
}

function removeOptionsAfter(select, fromValue, dayNum){
    select.innerHTML = selectOptionsTemplates[dayNum-1];

    var li_elements = select.getElementsByTagName("option");
    for (let i = 1; i < li_elements.length; i++) {

        if(fromValue == li_elements[i].innerHTML){
            select.removeChild(li_elements[i]);
            return;
        }
        else{
            select.removeChild(li_elements[i]);
            i--;
        }
    }
}


function hideUnavailableDaysTimesSelects(){
    var checkboxes =document.querySelector('#edit_booking_days');
    
    if(checkboxes){

        days_checkboxes_change(checkboxes);
    }
}

function days_checkboxes_change(checkboxes){
    var spans = checkboxes.getElementsByTagName('span');
    var ckecks = checkboxes.getElementsByTagName('input');

    for (let i = 0; i < spans.length; i++) {
        var span = spans[i];
        var check = ckecks[i];

        if(check.checked){

            edit_selects_display(span.innerHTML, 'show');
        }
        else{
            edit_selects_display(span.innerHTML, 'hide');
        }
    }
}

function edit_selects_display(week_day_abr, operation){
    
    var week_day = getFullWeekDayName(week_day_abr);
    
    var start_div = document.getElementsByName(week_day+'_start')[0].parentElement;
    var end_div = document.getElementsByName(week_day+'_end')[0].parentElement;

    var display = operation == 'hide' ? 'none' : 'inline-block';
    start_div.style.display = display;
    end_div.style.display = display;
}


function getFullWeekDayName(week_day_abr){

    switch (week_day_abr) {
        case 'Mon':
            return 'monday';
            break;
    
        case 'Tue':
            return 'tuesday';
            break;
        
        case 'Wed':
            return 'wednesday';
            break;

        case 'Thu':
            return 'thursday';
            break;

        case 'Fri':
            return 'friday';
            break;

        case 'Sat':
            return 'saturday';
            break;

        case 'Sun':
            return 'sunday';
            break;

        default:
            return '';
    }
}

</script>


<?php   

}

function setDefaultCheckboxes(){
    ?>
    <script>

        window.addEventListener('load', function () {
            
            var checkboxes =document.querySelector('#edit_booking_days');
            var ckecks = checkboxes.getElementsByTagName('input');

            for (let i = 0; i < ckecks.length; i++) {
                var check = ckecks[i];

                check.checked = true;

            }

            days_checkboxes_change(checkboxes);
        });
    </script>
    <?php
}

And i also made son changes on the style snippets, replace all old ones with this:


.hp-form__field--select{
    
    width: 45%;
}

.hp-form__field.hp-form__field--select{
    display: inline-block;
}

.hp-form__field.hp-form__field--select:nth-of-type(3) {
    float: right;
}

.hp-form__field--select{
    display: block;
}

.hp-field__label.hp-form__label small{
    display: none;
}

#edit_booking_min_time, #edit_booking_max_time {
    display: none;
}

#price_h_description, #tot_h_description{
    text-align: center;
    margin-bottom: 0;
}

IMPORTANT:
To make the week-day custom time work you have to add the listing attributes.
You have to create two attribues for each week day, one for the start time and one for the end time like this:

The end times have no title because i wrote the “END” label in the description of the attribute for styling pourposes
In start attributes you have to make the settings like this:

For the end attibutes make them like this:

For both start and end attributes the select should have this options:


(go on until 23:30)

Use the correct order for the attributes so they will be shown like this:

Make shure you have the booking availablility settings in the listings and not in the vendors setting.

For the services interval attribute create someting like this:

Also for this attributes the options will be 00:30, 01:00, 01:30…

To create the time options (from 00:00 to 23:30) I used a import plugin, tell me if you need help with that, I will try to explain it to you.

I hope is all clear, if not feel free to ask.
Have a nice weekend!

Hey!

Thank you so much for this code, this has saved me so much headache with my website! I greatly appreciate it, and I am sure many others will as well!

I am just wondering how can I make this code so that the user can select whatever end time they want (exactly how the start time works)? I tried to set the minimum service time to 0:00 as mentioned above, but it doesn’t give the dropdown of end times to select from.

Hi!

I didn’t tested with no min. service time because in my website you have to set at least 30 min of min service time.
If you want to set ‘00:00’ of min service time also, there was a bug that I fixed with just this one line before the return of the “getMaximumEnd” function:

Copy from here:

max_end = minimum_service_time == '00:00' ? '24:00' : max_end;

NOTE: if you don’t want to let users to select a min. service time you have also to set ‘00:00’ as default value (so the value will not be get from the listing parameters) like this:


(if you still want to make users select the min. service time you will just need the first change I told you above and to add ‘00:00’ option for min.service time).

Tell me if it worked, otherwise i’ll be happy to help you further!
Have a good day.

Emilio

Hi,
I found a bug in my code, make also this changes in removeBetween function:

copy from here the function:

function removeLiBetween(ul_el, li_elements, remove_start, remove_end){

            if(ul_el.getAttribute("removed_between"))
                return;
            
            for (let i = 0; i < li_elements.length; i++) {
                var li_value = li_elements[i].innerHTML;  
                
                if(compareTimes(li_value, '>=', remove_start)
                    && compareTimes(li_value, '<=', remove_end)){

                        ul_el.removeChild(li_elements[i]);
                        i--;
                }
            }
            
            if(ul_el.getAttribute("removed_between") == null){

                ul_el.setAttribute("removed_between", 'true');
            }
            else{
                ul_el.removeAttribute("removed_between");
            }
        }

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.