// (Step navigation handled by delegated handler above) $("#staff_btn").on("click", function (event) { event.preventDefault(); let staffFormData = $("#staff-form").serializeArray(); if ($("#staff-form").valid()) { let formDataCollection = {}; staffFormData.forEach(function (item) { formDataCollection[item.name] = item.value; }); $("#second-field").hide(); $("#third-field").show(); $("#progressbar li").eq(1).removeClass("active"); $("#progressbar li").eq(2).addClass("active"); } }); $("#image_prv").on("click", function () { $("#third-field").hide(); $("#first-field").show(); $("#progressbar li").eq(1).removeClass("active"); $("#progressbar li").eq(0).addClass("active"); }); $("#image-form").validate({ rules: { "service_images[]": { required: false, extension: "jpg|jpeg|png|svg|webp", }, service_video: { required: false, // Not required url: true, // Must be a valid URL }, }, messages: { "service_images[]": { required: "The service image is required.", extension: "Only JPG, JPEG, PNG, WEBP and SVG files are allowed.", }, service_video: { required: "The video URL is required.", // Customize if necessary url: "Please enter a valid URL for the video.", // Message for invalid URL format }, }, errorElement: "span", errorPlacement: function (error, element) { error.addClass("invalid-feedback"); element.closest(".mb-3").append(error); }, highlight: function (element) { $(element).addClass("is-invalid").removeClass("is-valid"); }, unhighlight: function (element) { $(element).removeClass("is-invalid").addClass("is-valid"); }, }); $("#image_btn").on("click", function (event) { event.preventDefault(); if ($("#image-form").valid()) { $("#third-field").hide(); $("#forth-field").show(); $("#progressbar li").eq(1).removeClass("active"); $("#progressbar li").eq(2).addClass("active"); } }); $("#seo_prv").on("click", function () { $("#forth-field").hide(); $("#third-field").show(); $("#progressbar li").eq(2).removeClass("active"); $("#progressbar li").eq(1).addClass("active"); }); $("#seo-form").validate({ rules: { seo_title: { required: true, maxlength: 255, }, }, messages: { seo_title: { required: "The SEO title field is required.", maxlength: "The SEO title cannot exceed 255 characters.", }, }, errorElement: "span", errorPlacement: function (error, element) { error.addClass("invalid-feedback"); element.closest(".mb-3").append(error); }, highlight: function (element) { $(element).addClass("is-invalid").removeClass("is-valid"); }, unhighlight: function (element) { $(element).removeClass("is-invalid").addClass("is-valid"); }, }); $("#seo_btn").on("click", function (event) { $("#serviceLoader").show(); event.preventDefault(); let serviceFormData = $("#service-form").serializeArray(); let staffFormData = $("#staff-form").serializeArray(); let locationFormData = $("#location-form").serializeArray(); let imageFormData = $("#location-form").serializeArray(); let seoFormData = $("#seo-form").serializeArray(); const all = 1; if (all == 1) { let finalFormData = new FormData(); finalFormData.append( "_token", $('meta[name="csrf-token"]').attr("content") ); [ ...serviceFormData, ...locationFormData, ...staffFormData, ...imageFormData, ...seoFormData, ].forEach(function (item) { finalFormData.append(item.name, item.value); }); let imageFiles = $("#service_images")[0].files; if (imageFiles.length > 0) { for (let i = 0; i < imageFiles.length; i++) { finalFormData.append("service_images[]", imageFiles[i]); // Append each file } } $("input[name='add_image[]']").each(function (index) { if ($(this)[0].files.length > 0) { finalFormData.append( `add_image[${index}]`, $(this)[0].files[0] ); } }); let serviceVideo = $("#service_video").val(); if (serviceVideo) { finalFormData.append("service_video", serviceVideo); } let serviceId = $("#service_id").val(); if (serviceId) { finalFormData.append("serviceId", serviceId); } let payload = []; $('input[name="branch_select[]"]:checked').each(function () { const branchId = $(this).val(); const staffIds = $( `input[name="staff_select[]"][data-branch-id="${branchId}"]:checked` ) .map(function () { return $(this).val(); }) .get(); payload.push({ branch_id: branchId, staff_ids: staffIds, }); }); finalFormData.append( "branch_staff_payload", JSON.stringify(payload) ); $.ajax({ url: "/provider/service/update", method: "POST", data: finalFormData, dataType: "json", contentType: false, processData: false, cache: false, beforeSend: function () { $(".add_btn").attr("disabled", true); $(".add_btn").html( '' ); }, }) .done((response, statusText, xhr) => { $(".error-text").text(""); $(".form-control").removeClass("is-invalid"); $(".add_btn").removeAttr("disabled"); $(".add_btn").html("Submit"); if (response.code === 200) { toastr.success(response.message); setTimeout(function () { window.location.href = response.redirect_url; }, 10); } }) .fail((error) => { $("#serviceLoader").hide(); $(".error-text").text(""); $(".form-control").removeClass("is-invalid"); $(".add_btn").removeAttr("disabled"); $(".add_btn").html("submit"); if (error.status == 422) { $.each(error.responseJSON, function (key, val) { $("#" + key).addClass("is-invalid"); $("#" + key + "_error").text(val[0]); }); } else { toastr.error(error.responseJSON.message); } }); } }); $("#addSlotBtn").on("click", function () { $("#slotData").slideDown(); }); $("#closeSlotBtn").on("click", function () { $("#slotData").slideUp(); }); const $priceType = $("#price_type"); const $hoursSection = $(".hours-section"); const $minutesSection = $(".minutes-section"); const $hoursSelect = $("#hours_select"); const $minutesSelect = $("#minutes_select"); $priceType.change(function () { const selectedValue = $(this).val(); $hoursSection.hide(); $minutesSection.hide(); $hoursSelect.val(""); $minutesSelect.val(""); if (selectedValue === "hourly") { $hoursSelect.val(1); $hoursSection.hide(); $minutesSection.hide(); } else if (selectedValue === "minute") { $minutesSection.show(); } else if ( selectedValue === "fixed" || "squre-metter" || "squre-Feet" ) { $hoursSelect.val(1); $hoursSection.show(); $minutesSection.hide(); } }); $(document).on("change", "input[name='day_checkbox[]']", function () { const checkbox = $(this); const container = checkbox.closest(".mb-4"); const day = container.attr("id").replace("Data", ""); if (checkbox.is(":checked")) { const initialTimeInput = `
`; container.find("#slotinputs").show().append(initialTimeInput); } }); $(document).on("click", ".add-time-btn", function () { const container = $(this).closest(".mb-4"); // Parent container for the day const day = container.attr("id").replace("Data", ""); // Extract the day const isChecked = container .find('input[type="checkbox"]') .is(":checked"); if (!isChecked) { return; } const newTimeInput = ` `; container.find("#slotinputs").append(newTimeInput); }); $(document).on("click", ".remove-time-btn", function () { $(this).closest(".additional-time").remove(); }); $(document).on("change", ".start_time", function () { const startTimeInput = $(this); const selectedHours = $("#hours_select").val(); // Assuming you have these selectors const selectedMinutes = $("#minutes_select").val(); // Assuming you have these selectors const startTime = startTimeInput.val(); const timeSlotDiv = startTimeInput.closest(".d-flex"); const endTimeInput = timeSlotDiv.find(".end_time"); // --- Step 1: Calculate End Time (Your existing logic) --- if ((selectedHours || selectedMinutes) && startTime) { const [startHour, startMinute] = startTime.split(":").map(Number); let endHour = startHour; let endMinute = startMinute; if (selectedHours) { endHour += parseInt(selectedHours); } if (selectedMinutes) { endMinute += parseInt(selectedMinutes); } endHour += Math.floor(endMinute / 60); endMinute = endMinute % 60; endHour = endHour % 24; const formattedEndTime = `${endHour .toString() .padStart(2, "0")}:${endMinute.toString().padStart(2, "0")}`; endTimeInput.val(formattedEndTime); } else { endTimeInput.val(""); return; // No start time or duration, so no validation needed } // --- Step 2: Validate for Overlapping Times (New Logic) --- const currentStartTime = startTimeInput.val(); const currentEndTime = endTimeInput.val(); const dayContainer = startTimeInput.closest(".mb-4"); let isOverlapping = false; if (!currentStartTime || !currentEndTime) return; // Iterate over all time slots within the same day dayContainer.find(".additional-time").each(function () { const otherSlot = $(this); // Don't compare the input with itself if (otherSlot.is(timeSlotDiv)) { return; // 'continue' in jQuery's .each() } const existingStartTime = otherSlot.find(".start_time").val(); const existingEndTime = otherSlot.find(".end_time").val(); if (existingStartTime && existingEndTime) { // Check for overlap: (StartA < EndB) && (StartB < EndA) if ( currentStartTime < existingEndTime && existingStartTime < currentEndTime ) { isOverlapping = true; return false; // 'break' in jQuery's .each() } } }); // --- Step 3: Handle the Overlap --- if (isOverlapping) { const dayName = dayContainer.attr("id").replace("Data", ""); toastr.error( "The selected time has already been occupied. Please select a different time." ); startTimeInput.val(""); // Clear the invalid start time endTimeInput.val(""); // Clear the calculated end time startTimeInput.focus(); // Set focus back to the input for correction } }); $('input[name="day_checkbox[]"]').on("change", function () { const parentDiv = $(this).closest(".mb-4"); const slotInputs = parentDiv.find("#slotinputs"); if ($(this).is(":checked")) { slotInputs.show(); } else { slotInputs.hide(); slotInputs.find(".start_time, .end_time").val(""); } }); $("#service_images").on("change", function (event) { const files = event.target.files; const previewContainer = $("#image_preview_container"); // previewContainer.empty(); Array.from(files).forEach((file, index) => { if (file.type.startsWith("image/")) { const reader = new FileReader(); reader.onload = function (e) { const imageHTML = ` `; previewContainer.append(imageHTML); }; reader.readAsDataURL(file); } });