/**
* Appointly Module - Shared helper functions
*/
/**
* Get available time slots for a date/provider/service combination
* @param {string} serviceId - The service ID
* @param {string} providerId - The provider ID
* @param {string} date - The date to check
* @param {string} currentStartHour - Current appointment start hour (for existing appointments)
* @param {string} timezone - Timezone to use
* @param {string} appointmentId - Current appointment ID when editing
*/
function appointlyGetTimeSlots(
serviceId,
providerId,
date,
currentStartHour,
timezone,
appointmentId
) {
// Show loading and disable time select
$("#slot_loading").removeClass("hide");
$("#available_times").prop("disabled", true);
// Hide any previous warnings
$(".time-slot-unavailable-info").addClass("hide");
// Determine if we're viewing the original appointment date (client-side check)
var appointmentDate =
$('input[name="appointment_date"]').data("current-date") || "";
var currentDate = $("#appointment_date").val() || "";
var clientSideIsOriginalDate =
appointmentDate && currentDate && appointmentDate === currentDate;
var requestData = {
service_id: serviceId,
provider_id: providerId,
date: date,
timezone: timezone,
appointment_id: appointmentId || 0,
rel_type: $("#rel_type").val() || "",
};
// Add CSRF token
requestData[csrfTokenName] = csrfTokenValue;
$.ajax({
url: admin_url + "appointly/appointments/get_available_time_slots",
type: "POST",
data: requestData,
dataType: "json",
success: function (response) {
$("#slot_loading").addClass("hide");
var $timeSelect = $("#available_times");
$timeSelect.html(
'<option value="">' +
appointlyLang.dropdown_non_selected_tex +
"</option>"
);
// Track if we have available slots
var hasAvailableSlots = false;
var hasUnavailableSlots = false;
var hasConflictWarnings = false; // Track slots with conflicts (staff appointments)
// Check if we're viewing the original date, either from server or our client-side check
var isViewingOriginalDate =
response.is_original_date || clientSideIsOriginalDate;
if (
response &&
response.success &&
response.time_slots &&
response.time_slots.length > 0
) {
// Process all time slots
$.each(response.time_slots, function (i, slot) {
var isAvailable = slot.available !== false;
var hasConflict = slot.has_conflict === true; // Check for conflict flag (staff appointments)
var isCurrentSlot =
currentStartHour && slot.value === currentStartHour;
// For editing, we always make the current slot available when on original date
if (isCurrentSlot && appointmentId && isViewingOriginalDate) {
isAvailable = true;
}
if (isAvailable) {
hasAvailableSlots = true;
// Track if this slot has a conflict warning
if (hasConflict) {
hasConflictWarnings = true;
}
// Create the option element
var optionText = slot.text;
// Add conflict warning to text for staff appointments
if (hasConflict && slot.unavailable_reason) {
optionText += " ⚠️ (" + slot.unavailable_reason + ")";
}
var $option = $("<option>", {
value: slot.value,
text: optionText,
selected: isCurrentSlot && isViewingOriginalDate,
});
// Add conflict class for styling
if (hasConflict) {
$option.addClass("time-slot-conflict-warning");
$option.attr("data-has-conflict", "true");
}
// Store end time if available
if (slot.end_time) {
$option.attr("data-end-time", slot.end_time);
}
if (isCurrentSlot && isViewingOriginalDate) {
$option.addClass("current-slot");
if (
typeof slot.text === "string" &&
!slot.text.includes("(current)")
) {
optionText = slot.text + " (current)";
if (hasConflict && slot.unavailable_reason) {
optionText += " ⚠️ (" + slot.unavailable_reason + ")";
}
$option.text(optionText);
}
}
$timeSelect.append($option);
} else {
// This is an unavailable slot
hasUnavailableSlots = true;
var reason = slot.unavailable_reason
? " - " + slot.unavailable_reason
: "";
var $option = $("<option>", {
value: "", // Empty value so it can't be selected
disabled: true,
class: "unavailable-time-slot",
text: slot.text + reason,
});
$timeSelect.append($option);
}
});
// Enable select if we have available slots
if (hasAvailableSlots) {
$timeSelect.prop("disabled", false);
} else {
$timeSelect.prop("disabled", true);
}
// Initialize selectpicker
$timeSelect.selectpicker("refresh");
// Show warnings for unavailable slots or conflict warnings
if (hasUnavailableSlots) {
// If the warning element doesn't exist, create it
if ($(".time-slot-unavailable-info").length === 0) {
// Create a separate warning message outside the dropdown
$timeSelect
.closest(".form-group")
.after(
'<div class="time-slot-unavailable-info alert alert-danger mtop10"><i class="fa fa-info-circle"></i> ' +
appointlyLang.appointment_unavailable_slots_shown +
"</div>"
);
} else {
// Update existing warning with the correct message and make it visible
$(".time-slot-unavailable-info")
.removeClass("hide")
.removeClass("alert-warning")
.addClass("alert-danger")
.html(
'<i class="fa fa-info-circle"></i> ' +
appointlyLang.appointment_unavailable_slots_shown
);
}
// Style unavailable slots
setTimeout(styleTimeSlotOptions, 200);
} else if (hasConflictWarnings) {
// Show warning message for staff appointments with conflicts
var conflictMessage =
"Some time slots have scheduling conflicts. You can still book them, but the assigned staff may already have an appointment.";
if ($(".time-slot-unavailable-info").length === 0) {
$timeSelect
.closest(".form-group")
.after(
'<div class="time-slot-unavailable-info alert alert-warning mtop10"><i class="fa fa-exclamation-triangle"></i> ' +
conflictMessage +
"</div>"
);
} else {
$(".time-slot-unavailable-info")
.removeClass("hide")
.removeClass("alert-danger")
.addClass("alert-warning")
.html(
'<i class="fa fa-exclamation-triangle"></i> ' + conflictMessage
);
}
} else {
$(".time-slot-unavailable-info").addClass("hide");
}
// If we have no available slots but have unavailable ones, show message
if (!hasAvailableSlots && hasUnavailableSlots) {
$timeSelect.html(
'<option value="">' +
appointlyLang.appointment_no_slots_available +
"</option>"
);
$timeSelect.prop("disabled", true).selectpicker("refresh");
// Show a warning message outside the dropdown
if ($(".time-slot-unavailable-info").length === 0) {
$timeSelect
.closest(".form-group")
.after(
'<div class="time-slot-unavailable-info alert alert-warning mtop10"><i class="fa fa-info-circle"></i> ' +
appointlyLang.appointment_no_slots_available +
"</div>"
);
} else {
$(".time-slot-unavailable-info")
.removeClass("hide")
.removeClass("alert-danger")
.addClass("alert-warning")
.html(
'<i class="fa fa-info-circle"></i> ' +
appointlyLang.appointment_no_slots_available
);
}
}
} else {
// No slots available
$timeSelect.html(
'<option value="">' +
appointlyLang.appointment_no_slots_available +
"</option>"
);
$timeSelect.prop("disabled", true).selectpicker("refresh");
// Show no slots available message outside the dropdown
if ($(".time-slot-unavailable-info").length === 0) {
$timeSelect
.closest(".form-group")
.after(
'<div class="time-slot-unavailable-info alert alert-warning mtop10"><i class="fa fa-info-circle"></i> ' +
appointlyLang.appointment_no_slots_available +
"</div>"
);
} else {
$(".time-slot-unavailable-info")
.removeClass("hide")
.removeClass("alert-danger")
.addClass("alert-warning")
.html(
'<i class="fa fa-info-circle"></i> ' +
appointlyLang.appointment_no_slots_available
);
}
if (response && response.message) {
alert_float("warning", response.message);
}
}
// After initialization, style the selectpicker items
setTimeout(styleTimeSlotOptions, 200);
},
error: function (xhr, status, error) {
$("#slot_loading").addClass("hide");
var $timeSelect = $("#available_times");
$timeSelect.html(
'<option value="">' +
appointlyLang.appointment_error_loading_slots +
"</option>"
);
$timeSelect.prop("disabled", true).selectpicker("refresh");
alert_float("danger", appointlyLang.appointment_error_loading_slots);
},
});
}
/**
* Style time slot options in the selectpicker dropdown
*/
function styleTimeSlotOptions() {
// Apply styling to unavailable time slots in the dropdown
$(".unavailable-time-slot").parents("li").css({
color: "#ff6666",
"background-color": "#ffeeee",
"text-decoration": "line-through",
"font-style": "italic",
});
// Apply styling to current slot in the dropdown
$(".current-slot").parents("li").css({
"font-weight": "bold",
"background-color": "#e8f0fe",
});
// Show warning message if we have unavailable slots
if ($(".unavailable-time-slot").length > 0) {
$(".alert.alert-warning.time-slot-unavailable-info").removeClass("hide");
}
}
/**
* Handle time slot selection change
* @param {jQuery} $timeSelect - The time select element
*/
function handleTimeSlotChange($timeSelect) {
var selectedTime = $timeSelect.val();
if (selectedTime) {
// Store the start hour in the hidden input
$('input[name="start_hour"]').val(selectedTime);
// Get the end time if it's stored in data attributes
var endTime = $timeSelect.find("option:selected").data("end-time");
if (endTime) {
$('input[name="end_hour"]').val(endTime);
} else {
// Calculate end time based on duration if we have it
var duration = $("#appointment_duration").val();
if (duration) {
var startTimestamp = new Date(
"1970-01-01T" + selectedTime + ":00Z"
).getTime();
var endTimestamp = startTimestamp + duration * 60 * 1000;
var endTimeStr = new Date(endTimestamp).toISOString().substr(11, 5);
$('input[name="end_hour"]').val(endTimeStr);
}
}
} else {
$('input[name="start_hour"]').val("");
$('input[name="end_hour"]').val("");
}
}
/**
* Load providers for a service
* @param {string} serviceId - The service ID
*/
function appointlyLoadProviders(serviceId) {
if (!serviceId) return;
$("#provider_loading").removeClass("hide");
var providerSelect = $("#provider_id");
providerSelect.prop("disabled", true);
var data = {
service_id: serviceId,
};
// Add CSRF token
data[csrfTokenName] = csrfTokenValue;
$.ajax({
url: admin_url + "appointly/appointments/get_providers_by_service",
type: "POST",
data: data,
success: function (response) {
$("#provider_loading").addClass("hide");
providerSelect.html(
'<option value="">' +
appointlyLang.appointment_select_provider +
"</option>"
);
if (response && response.length > 0) {
var primaryProvider = null;
$.each(response, function (i, provider) {
var selected = "";
if (provider.is_primary == 1) {
selected = "selected";
primaryProvider = provider.id;
}
providerSelect.append(
'<option value="' +
provider.id +
'" ' +
selected +
">" +
provider.name +
"</option>"
);
});
providerSelect.prop("disabled", false).selectpicker("refresh");
// If there's a primary provider selected, trigger change to load dates
if (primaryProvider) {
providerSelect.trigger("change");
}
} else {
providerSelect.selectpicker("refresh");
}
},
});
}
/**
* Format date string for API calls
* @param {string} dateStr - Date string in local format
* @returns {string} Date string in YYYY-MM-DD format
*/
function appointlyFormatDate(dateStr) {
if (!dateStr) return "";
// Try to parse the date using the app's date format
var parsedDate = moment(dateStr, app.options.date_format.toUpperCase());
// Check if parsing was successful
if (!parsedDate.isValid()) {
// Try common date formats as fallback
var formats = [
"DD-MM-YYYY",
"MM-DD-YYYY",
"YYYY-MM-DD",
"DD/MM/YYYY",
"MM/DD/YYYY",
"DD.MM.YYYY",
"MM.DD.YYYY",
];
for (var i = 0; i < formats.length; i++) {
parsedDate = moment(dateStr, formats[i]);
if (parsedDate.isValid()) {
break;
}
}
}
// If we still don't have a valid date, return original string
if (!parsedDate.isValid()) {
return dateStr;
}
// Return the date in YYYY-MM-DD format
return parsedDate.format("YYYY-MM-DD");
}
/**
* Reset time selection dropdown
* @param {string} message - Optional custom message to display
*/
function resetTimeSelection(message) {
var timesSelect = $("#available_times");
var displayMessage = message || appointlyLang.appointment_date_required;
timesSelect.html('<option value="">' + displayMessage + "</option>");
timesSelect.prop("disabled", true).selectpicker("refresh");
// Hide any warnings
$(".time-slot-unavailable-info").addClass("hide");
}
/**
* Reset time selection with service/provider required message
*/
function resetTimeSelectionRequireServiceProvider() {
var message =
appointlyLang.appointment_select_service_provider_first ||
"Please select service and provider first";
resetTimeSelection(message);
// Focus on service field to guide user
setTimeout(function () {
var $serviceField = $("#service_id");
if ($serviceField.length && $serviceField.is(":visible")) {
$serviceField.focus();
// If using selectpicker, open the dropdown
if ($serviceField.data("selectpicker")) {
$serviceField.selectpicker("toggle");
}
}
}, 100);
}
/**
* Reset date and time selections
*/
function resetDateAndTimeSelections() {
$("#appointment_date").val("");
resetTimeSelection();
}
/**
* Handle appointment type change
* @param {string} relType - The relationship type (internal, external, lead_related, internal_staff)
* @param {boolean} isInit - Whether this is during initialization
*/
function appointlyHandleTypeChange(relType, isInit) {
// Clear fields when switching types (but not during initialization)
if (!isInit) {
$("#contact_id, #rel_id").val("").selectpicker("refresh");
$("#name, #email, #phone").val("");
}
// Handle each appointment type
switch (relType) {
case "internal_staff":
// Staff-only appointments
$(".staffonly-hide").hide();
$("#service_id, #provider_id").closest(".form-group").hide();
$("#service_id, #provider_id")
.val("")
.prop("disabled", true)
.selectpicker("refresh");
$("#duration_wrapper, .staff-only-fields").removeClass("hidden");
// Hide all contact-related fields
$(
"#select_contacts, #rel_id_wrapper, #div_name, #div_email, #div_phone"
).addClass("hidden");
$("#contact_id, #rel_id").val("").selectpicker("refresh");
$("#name, #email, #phone").val("");
// Clear any service/provider validation messages and reload time slots if date is selected
var selectedDate = $("#appointment_date").val();
if (selectedDate) {
// For staff-only, immediately load time slots without validation
$("#slot_loading").removeClass("hide");
var providerId = $('input[name="created_by"]').val() || "1";
var formattedDate = appointlyFormatDate
? appointlyFormatDate(selectedDate)
: selectedDate;
appointlyGetTimeSlots(
0,
providerId,
formattedDate,
"",
$("#timezone").val() || "",
0
);
} else {
// Clear validation message and reset to normal state
resetTimeSelection();
}
break;
case "internal":
// Internal contact appointments
$(".staffonly-hide").show();
$("#service_id, #provider_id").closest(".form-group").show();
$("#service_id, #provider_id")
.prop("disabled", false)
.selectpicker("refresh");
$("#related_field_wrapper, #select_contacts").removeClass("hidden");
// Hide other type-specific fields
$(
"#rel_id_wrapper, #div_name, #div_email, #div_phone, #duration_wrapper"
).addClass("hidden");
$("#rel_id").val("").selectpicker("refresh");
$("#name, #email, #phone").val("");
// Don't clear contact_id during initialization to preserve pre-selected values
if (!isInit) {
$("#contact_id").val("").selectpicker("refresh");
}
// Clear any previous validation messages
resetTimeSelection();
break;
case "lead_related":
// Lead-related appointments
$(".staffonly-hide").show();
$("#service_id, #provider_id").closest(".form-group").show();
$("#service_id, #provider_id")
.prop("disabled", false)
.selectpicker("refresh");
$("#related_field_wrapper, #rel_id_wrapper").removeClass("hidden");
$("#rel_id_select").show();
$("#rel_id").prop("disabled", false);
initLeadSearch();
// Hide other type-specific fields
$(
"#select_contacts, #div_name, #div_email, #div_phone, #duration_wrapper"
).addClass("hidden");
$("#contact_id").val("").selectpicker("refresh");
$("#name, #email, #phone").val("");
// Clear any previous validation messages
resetTimeSelection();
break;
case "external":
// External appointments
$(".staffonly-hide").show();
$("#service_id, #provider_id").closest(".form-group").show();
$("#service_id, #provider_id")
.prop("disabled", false)
.selectpicker("refresh");
$(
"#related_field_wrapper, #div_name, #div_email, #div_phone"
).removeClass("hidden");
$("#div_name input, #div_email input, #div_phone input")
.prop("disabled", false)
.prop("readonly", false);
// Hide other type-specific fields
$("#select_contacts, #rel_id_wrapper, #duration_wrapper").addClass(
"hidden"
);
$("#contact_id, #rel_id").val("").selectpicker("refresh");
// Clear any previous validation messages
resetTimeSelection();
break;
default:
// Fallback for unknown types
$(".staffonly-hide").show();
$("#service_id, #provider_id").closest(".form-group").show();
$("#service_id, #provider_id")
.prop("disabled", false)
.selectpicker("refresh");
$("#related_field_wrapper").removeClass("hidden");
break;
}
// Refresh all selectpickers
$("select").selectpicker("refresh");
// Update the relation type selector if not initializing
if (!isInit) {
$("#rel_type").val(relType).selectpicker("refresh");
}
}
/**
* Initialize lead search functionality
*/
function initLeadSearch() {
setTimeout(function () {
if (typeof init_ajax_search === "function") {
// Force destroy any existing search first
if ($("#rel_id").data("search")) {
$("#rel_id").data("search").destroy();
}
// Initialize lead search
init_ajax_search("lead", $("#rel_id"), {
rel_id: $("#rel_id").val(),
type: "leads",
});
$("#rel_id_wrapper").removeClass("hide");
$("#rel_id_select").show();
$("#rel_id").selectpicker("refresh");
}
}, 300);
}
/**
* Fetch contact data for a lead or contact
* @param {number} contactId - The contact ID
* @param {boolean} isLead - Whether this is a lead
*/
function appointlyFetchContactData(contactId, isLead) {
if (!contactId) return;
// Show loading indicator
$("#provider_loading").removeClass("hide");
var data = {
contact_id: contactId,
lead: isLead ? 1 : 0,
};
// Add CSRF token
data[csrfTokenName] = csrfTokenValue;
$.ajax({
url: admin_url + "appointly/appointments/fetch_contact_data",
type: "POST",
data: data,
dataType: "json",
success: function (response) {
$("#provider_loading").addClass("hide");
if (response) {
// Make fields visible and read-only (not disabled!)
$("#div_name, #div_email, #div_phone").removeClass("hidden");
$("#div_name input, #div_email input, #div_phone input")
.prop("disabled", false)
.prop("readonly", true);
// Get contact details
var fullName = "";
if (typeof response.firstname !== "undefined") {
fullName = response.firstname + " " + response.lastname;
} else if (typeof response.name !== "undefined") {
fullName = response.name;
}
var email = response.email || "";
var phone = response.phonenumber || response.phone || "";
var address = response.address || "";
// Set field values
$("#name").val(fullName);
$("#email").val(email);
$("#phone").val(phone);
// Set location/address if available
if (address && $("#location").val() === "") {
$("#location").val(address);
}
} else {
// Reset contact fields if no data
appointlyResetContactFields();
}
},
});
}
/**
* Reset contact fields (name, email, phone)
*/
function appointlyResetContactFields() {
// Hide all fields
$("#div_name, #div_email, #div_phone").addClass("hidden");
// Reset values
$("#name, #email, #phone").val("");
}
/**
* Format time for display based on app time format
* @param {string} timeValue - Time in 24-hour format (HH:MM)
* @param {string} endTimeValue - Optional end time for range display
* @returns {string} Formatted time string
*/
function appointlyFormatTimeDisplay(timeValue, endTimeValue) {
if (!timeValue) return "";
var formattedTime = "";
// Check if we should use 24-hour format - default to 12-hour (AM/PM) format
var use24Hour = false;
if (app && app.options && app.options.time_format == 24) {
use24Hour = true;
}
if (!use24Hour) {
// 12-hour format with AM/PM (default)
var timeParts = timeValue.split(":");
var hour = parseInt(timeParts[0], 10);
var minute = timeParts[1];
var period = hour >= 12 ? "PM" : "AM";
// Convert hour to 12-hour format
hour = hour % 12;
hour = hour ? hour : 12; // Convert 0 to 12
formattedTime = hour + ":" + minute + " " + period;
// Add end time if provided
if (endTimeValue) {
var endTimeParts = endTimeValue.split(":");
var endHour = parseInt(endTimeParts[0], 10);
var endMinute = endTimeParts[1];
var endPeriod = endHour >= 12 ? "PM" : "AM";
endHour = endHour % 12;
endHour = endHour ? endHour : 12;
formattedTime += " - " + endHour + ":" + endMinute + " " + endPeriod;
}
} else {
// 24-hour format
formattedTime = timeValue;
// Add end time if provided
if (endTimeValue) {
formattedTime += " - " + endTimeValue;
}
}
return formattedTime;
}
/**
* Configure date picker with past dates handling and blocked days
* @param {Array} blockedDays - Array of blocked days in YYYY-MM-DD format
* @param {boolean} disablePastDates - Whether to show past dates
* @param {string} relType - Appointment relationship type
* @param {Object} providerSchedule - Optional provider schedule data
* @return {Object} The date picker options
*/
function appointlyConfigureDatePicker(
blockedDays,
disablePastDates,
relType,
providerSchedule
) {
// Default options
var options = {
dayOfWeekStart: app.options.calendar_first_day,
format: app.options.date_format,
timepicker: false,
scrollInput: false,
validateOnBlur: false,
};
// Set minDate based on disablePastDates setting
if (disablePastDates) {
options.minDate = 0;
}
// Add beforeShowDay function to handle blocked days AND company schedule
options.beforeShowDay = function (date) {
// If staff-only, allow all days (staff meetings can be scheduled anytime)
if (relType === "internal_staff") {
return [""];
}
// Format date to YYYY-MM-DD for comparison
var formattedDate =
date.getFullYear() +
"-" +
padZero(date.getMonth() + 1) +
"-" +
padZero(date.getDate());
// Check if the date is in the blocked days array
if (blockedDays && blockedDays.indexOf(formattedDate) !== -1) {
return ["blocked-date"];
}
// Check provider schedule first if available
if (providerSchedule) {
var dayOfWeek = date.getDay(); // 0 = Sunday, 1 = Monday, etc.
// Convert JS day (0=Sunday) to provider schedule day number (1=Monday, 7=Sunday)
var providerDayNumber;
if (dayOfWeek === 0) {
providerDayNumber = 7; // Sunday
} else {
providerDayNumber = dayOfWeek; // Monday-Saturday
}
// Check if provider is available on this day
var providerDaySchedule = providerSchedule[providerDayNumber];
if (providerDaySchedule && !providerDaySchedule.enabled) {
return ["provider-unavailable"];
}
}
// Check company schedule for this day of week
var dayOfWeek = date.getDay(); // 0 = Sunday, 1 = Monday, etc.
var dayNames = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
var dayName = dayNames[dayOfWeek];
// Check if we have company schedule data available
if (
typeof window.appointlyCompanySchedule !== "undefined" &&
window.appointlyCompanySchedule
) {
var daySchedule = window.appointlyCompanySchedule[dayName];
if (daySchedule && !daySchedule.is_enabled) {
return ["company-closed"];
}
}
return [""];
};
// Add onGenerate event to style blocked dates and company closed days
options.onGenerate = function (ct) {
setTimeout(function () {
// Add proper tooltips to all date cells
$(".xdsoft_date").each(function () {
var $this = $(this);
var dateData = $this.data();
if (dateData) {
// Format as YYYY-MM-DD for comparison
var month = parseInt(dateData.month) + 1; // months are 0-based
var dateStr =
dateData.year +
"-" +
("0" + month).slice(-2) +
"-" +
("0" + dateData.date).slice(-2);
// Create a proper Date object for this cell
var jsDate = new Date(dateData.year, dateData.month, dateData.date);
var dayOfWeek = jsDate.getDay(); // 0 = Sunday, 1 = Monday, etc.
// Check if this is a past date
var isPastDate = jsDate < new Date().setHours(0, 0, 0, 0);
// Check if date is blocked
var isBlocked = blockedDays && blockedDays.indexOf(dateStr) !== -1;
// Check if provider is unavailable on this day
var isProviderUnavailable = false;
if (providerSchedule) {
var providerDayNumber = dayOfWeek === 0 ? 7 : dayOfWeek; // Convert to 1-7 format
var providerDaySchedule = providerSchedule[providerDayNumber];
isProviderUnavailable =
providerDaySchedule && !providerDaySchedule.enabled;
}
// Check if company is closed on this day
var isCompanyClosed = false;
if (
typeof window.appointlyCompanySchedule !== "undefined" &&
window.appointlyCompanySchedule
) {
var dayNames = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
var dayName = dayNames[dayOfWeek];
var daySchedule = window.appointlyCompanySchedule[dayName];
isCompanyClosed = daySchedule && !daySchedule.is_enabled;
}
if (isBlocked) {
$this.attr(
"title",
appointlyLang.appointment_blocked_days ||
"Company Holiday/Blocked Date"
);
$this.addClass("xdsoft_disabled blocked-date");
$this.css("cursor", "not-allowed");
// Add the lock icon
if (!$this.find(".blocked-icon").length) {
$this.append('<i class="fa fa-lock blocked-icon"></i>');
}
} else if (isProviderUnavailable && !isPastDate) {
$this.addClass("xdsoft_disabled provider-unavailable");
$this.attr(
"title",
appointlyLang.appointment_provider_unavailable ||
"Provider does not work on this day"
);
// $this.css('cursor', 'not-allowed');
} else if (isCompanyClosed) {
$this.addClass("xdsoft_disabled company-closed");
$this.attr("title", "Company is closed on this day");
$this.css("cursor", "not-allowed");
} else if (isPastDate) {
$this.attr("title", "Past date");
$this.addClass("xdsoft_disabled");
// $this.css('cursor', 'not-allowed');
$this.removeClass("xdsoft_current");
} else if (!$this.attr("title")) {
$this.attr(
"title",
appointlyLang.appointment_available_days ||
"Available for booking"
);
}
}
});
}, 100);
};
return options;
}
// Helper function to pad numbers with leading zeros
function padZero(num) {
return (num < 10 ? "0" : "") + num;
}
/**
* Toggle visibility of optional field in forms
* @param {string} fieldId - The ID of the field container to show
* @param {HTMLElement} linkElement - The link element that was clicked
*/
function appointlyToggleOptionalField(fieldId, linkElement) {
// Show the field container
document.getElementById(fieldId).style.display = "block";
// Hide the link that was clicked
linkElement.style.display = "none";
// Focus on the first input/select/textarea in the revealed container
const firstInput = document
.getElementById(fieldId)
.querySelector("input, select, textarea");
if (firstInput) {
firstInput.focus();
}
return false;
}