/home/edulekha/crm.edulekha.com/modules/appointly/assets/js/index_main_js.php
<!--suppress JSJQueryEfficiency -->
<script>
var appointly_please_wait = "<?= _l("wait_text"); ?>";
var appointly_lang_finished = "<?= _l('appointment_marked_as_finished'); ?>";
var appointly_lang_approved = "<?= _l('appointment_marked_as_approved'); ?>";
var appointly_lang_cancelled = "<?= _l('appointment_is_cancelled'); ?>";
var appointly_lang_no_show = "<?= _l('appointment_marked_as_no_show'); ?>";
var appointly_mark_as_ongoing = "<?= _l('appointment_marked_as_ongoing'); ?>";
var appointly_are_you_sure_mark_as_cancelled = "<?= _l('appointment_are_you_sure_to_cancel'); ?>";
var appointly_are_you_sure_mark_as_no_show = "<?= _l('appointment_are_you_sure_mark_as_no_show'); ?>";
var appointment_are_you_sure_mark_as_ongoing = "<?= _l('appointment_are_you_sure_to_mark_as_ongoing'); ?>";
var appointly_are_you_early_reminders = "<?= _l('appointly_are_you_early_reminders'); ?>";
var appointly_reminders_sent = "<?= _l('appointly_reminders_sent'); ?>";
var filters = <?php echo json_encode($filters); ?>;
var customBlockedDates = <?php
$bd = get_option('appointly_blocked_days');
echo $bd ? json_encode(json_decode($bd, true)) : '[]';
?>;
$(function() {
$(document).on('click', '.finished-no-click-action', function(e) {
e.preventDefault();
e.stopPropagation();
});
var apointmentsServerParams = {};
// Get all filters as params
for (var filter in filters) {
apointmentsServerParams[filters[filter]] = "[name=\"" + filters[filter] + "\"]";
}
// custom parameter for Google Calendar filter
apointmentsServerParams['custom_view'] = function() {
return $('input[name="custom_view"]').val();
};
let appointmentsTable = initDataTable(".table-appointments",
admin_url + 'appointly/appointments/table',
[],
[6], // No non-sortable columns
apointmentsServerParams,
[2, "desc"], // Default sort by appointment date, descending
undefined, {
// Error handling for DataTables
"error": function(xhr, error, thrown) {
console.error("DataTables error:", error, thrown);
alert_float('danger', '<?= _l('appointment_error_loading_details') ?>: ' + (thrown || '<?= _l('appointment_unknown_error') ?>'));
// Prevent DataTables from showing error alert
return true;
},
// Better error handling for JSON parsing issues
"ajax": {
"dataSrc": function(json) {
if (json && json.data) {
return json.data;
} else if (json && json.aaData) {
return json.aaData;
} else {
console.log("Invalid JSON response structure:", json);
return [];
}
},
"error": function(xhr, error, thrown) {
console.error("AJAX error:", error, thrown);
if (xhr.responseText) {
console.log("Response:", xhr.responseText.substring(0, 1000));
}
// Return empty dataset to prevent DataTables error
return {
data: []
};
}
}
}
);
$(document).ready(function() {
// Apply to any table with the appointments table class
$(document).on('draw.dt', '.appointments-table', function() {
// For each row in the table
$(this).find('tbody tr').each(function() {
// Check if this is a Google synced appointment by looking for:
// 1. Google event ID element or
// 2. Google icon indicator or
// 3. Data attribute indicating Google source
var isGoogleSynced = $(this).find('.google-event-id').length > 0 ||
$(this).find('.google-calendar-icon').length > 0 ||
$(this).data('source') === 'google_calendar';
if (isGoogleSynced) {
// Find and disable ALL dropdowns in this row
$(this).find('select').prop('disabled', true)
.addClass('google-synced-disabled')
.attr('title', 'Changes not available for Google Calendar events');
// Also disable any action buttons if needed
$(this).find('.dropdown-toggle[data-toggle="dropdown"]').addClass('disabled')
.attr('title', 'Actions limited for Google Calendar events');
}
});
});
});
$("body").on("click", ".approve_appointment", function() {
$(this).attr("disabled", true);
$(this).prev().next().addClass("approve_appointment_spacing");
$(this).html("<i class=\"fa fa-refresh fa-spin fa-fw\"></i>");
});
$("#createNewAppointment").click(function() {
createNewAppointment();
});
function createNewAppointment(type = null) {
// Redirect to standalone create page
let url = admin_url + 'appointly/appointments/create_page';
// If type is specified, add it as a query parameter
if (type) {
url += '?rel_type=' + type;
}
window.location.href = url;
}
// filter click handler
$('body').on('click', '.filter-group a', function(e) {
e.preventDefault();
var customView = $(this).data('cview');
$('input[name="custom_view"]').val(customView);
// Update active state
$('.filter-group').removeClass('active');
$(this).closest('.filter-group').addClass('active');
// Reload table with filter
appointmentsTable.ajax.reload();
});
});
$(".modal").on("hidden.bs.modal", function(e) {
$(this).removeData();
});
var todaysDate = new Date();
var currentDate = todaysDate.getFullYear() + "-" + (((todaysDate.getMonth() + 1) < 10) ? "0" : "") + (todaysDate
.getMonth() + 1 + "-" + ((todaysDate.getDate() < 10) ? "0" : "") + todaysDate.getDate());
// Update the busy times check to use the selected duration
function checkTimeSlotAvailability(startTime, duration, busyTimes) {
var start = moment(startTime, 'HH:mm');
var end = moment(startTime, 'HH:mm').add(duration || 60, 'minutes');
for (var i = 0; i < busyTimes.length; i++) {
var busyStart = moment(busyTimes[i].start_hour, 'HH:mm');
var busyEnd = moment(busyTimes[i].start_hour, 'HH:mm')
.add(busyTimes[i].duration || 60, 'minutes');
// Check if slots overlap
if ((start.isSameOrAfter(busyStart) && start.isBefore(busyEnd)) ||
(end.isAfter(busyStart) && end.isSameOrBefore(busyEnd)) ||
(start.isBefore(busyStart) && end.isAfter(busyEnd))) {
return false;
}
}
return true;
}
// Create a new task directly from relation, related options selected after modal is shown
function new_task_from_relation_appointment(data) {
var url = admin_url + 'tasks/task';
// Initialize new task modal
new_task(url);
// When task modal is shown
$('#_task').on('show.bs.modal', function(e) {
var $modal = $(this);
// Set task name with appointment subject
$modal.find('input[name="name"]').val(data.subject);
// set hourly rate to task modal
console.log(data.service_price);
$modal.find('input[name="hourly_rate"]').val(data.service_price);
// set description to task modal
var description = data.description;
// Handle description field (might be TinyMCE or regular textarea)
if (typeof(tinymce) != 'undefined') {
var descEditor = tinymce.get('description');
if (descEditor) {
descEditor.setContent(description);
}
} else {
$modal.find('textarea[name="description"]').val(description);
}
// Set start date to appointment date
$modal.find('input[name="startdate"]').val(data.date);
// Set priority (if needed)
$modal.find('select[name="priority"]').val(2).trigger('change');
// Assign to appointment creator
if (data.created_by) {
$modal.find('select[name="assignees[]"]')
.val(data.created_by)
.trigger('change');
}
// Mark as not billable by default
$modal.find('input[name="billable"]').prop('checked', false);
// Pre-fill client/prospect information based on appointment source
if (data.source && data.contact_id) {
var relType = '';
var relId = '';
// Determine relationship type and ID based on appointment source
if (data.source === 'internal') {
// Internal appointment with existing client contact
relType = 'customer';
relId = data.contact_id;
} else if (data.source === 'lead_related') {
// Lead-related appointment
relType = 'lead';
relId = data.contact_id;
} else if (data.source === 'external' && data.name) {
// External appointment - could be converted to lead
if (data.contact_id) {
relType = 'lead';
relId = data.contact_id;
}
}
// Set the relationship type if we have one
if (relType) {
$modal.find('select[name="rel_type"]').val(relType).trigger('change');
// Wait a moment for the rel_id field to be populated, then set the ID
setTimeout(function() {
if (relId) {
// For customer relationships, we need to get the client ID from contact ID
if (relType === 'customer') {
// Use the existing helper function to get client ID from contact ID
$.ajax({
url: admin_url + 'appointly/appointments/fetch_contact_data',
type: 'POST',
data: {
contact_id: relId,
is_lead: 'false'
},
dataType: 'json',
success: function(response) {
if (response.success && response.data && response.data.userid) {
$modal.find('select[name="rel_id"]').val(response.data.userid).trigger('change');
}
},
error: function() {
console.log('Could not get client ID from contact ID');
}
});
} else {
// For leads, use the contact_id directly
$modal.find('select[name="rel_id"]').val(relId).trigger('change');
}
}
}, 500);
}
}
// Refresh any select2 elements
$modal.find('select').trigger('change');
});
}
// Init lead convert to lead for appointment
function init_appointment_lead(data) {
// Initialize the lead modal
if (init_lead_modal_data(undefined, undefined, false)) {
$('#lead-modal').modal('show');
}
// When modal is shown, populate the fields
$('#lead-modal').on('shown.bs.modal', function(e) {
var $modal = $(this);
// Set modal title
$modal.find('.add-title').text(app.lang.appointments_convert_to_lead);
// Basic info
$modal.find('input[name="name"]').val(data.name);
$modal.find('input[name="email"]').val(data.email);
$modal.find('input[name="phonenumber"]').val(data.phone);
// Additional fields
$modal.find('input[name="address"]').val(data.address);
$modal.find('input[name="city"]').val(data.city);
$modal.find('input[name="state"]').val(data.state);
$modal.find('input[name="country"]').val(data.country);
$modal.find('input[name="zip"]').val(data.zip);
// Enhanced description with appointment details
var description = "<?= _l('appointment_label') ?>" + ': ' + data.subject + '\n\n';
// Add appointment details
if (data.service_name) {
description += "<?= _l('appointment_service') ?>" + ': ' + data.service_name + '\n';
}
if (data.date) {
description += "<?= _l('appointment_date') ?>" + ': ' + data.date + '\n';
}
description += '\n' + (data.description || '');
$modal.find('textarea[name="description"]').val(description);
// Set source if exists (Website = 2, Phone = 1, etc.)
var $sourceSelect = $modal.find('select[name="source"]');
if ($sourceSelect.length) {
$sourceSelect.val(1).trigger('change');
}
// Set status if exists (New = 2, Contacted = 1, etc.)
var $statusSelect = $modal.find('select[name="status"]');
if ($statusSelect.length) {
$statusSelect.val(2).trigger('change');
}
// Set company name if available (use name as company for external appointments)
var $companyField = $modal.find('input[name="company"]');
if ($companyField.length && data.name) {
$companyField.val(data.name);
}
// Refresh any select2 elements
$modal.find('select').trigger('change');
});
}
// Request appointment feedback
function request_appointment_feedback(appointment_id) {
$("body").append("<div class=\"dt-loader\"></div>");
var url = admin_url + "appointly/appointments/requestAppointmentFeedback/" + appointment_id;
$.post(url).done(function(response) {
if (response.success == true) {
alert_float("info", "<?= _l("appointment_feedback_requested_alert"); ?>");
$("body").find(".dt-loader").remove();
}
}).fail(function(err) {
console.log(err)
$("body").find(".dt-loader").remove();
console.log("An unknown error has been thrown" + err);
});
}
// Delete appointment from database
function deleteAppointment(id, element) {
if (confirm("<?= _l('appointment_are_you_sure'); ?>")) {
// Check if it's a Google synced appointment
var isGoogleSynced = $(element).closest('tr').find('.fab.fa-google').length > 0 ||
!$.isNumeric(id); // Google IDs are typically not numeric
if (isGoogleSynced) {
// For Google synced appointments, we need to delete from Google Calendar
$.ajax({
url: admin_url + "appointly/appointments/deleteGoogleSyncedAppointment/" + id,
type: 'POST',
dataType: 'json',
beforeSend: function() {
$(".table-appointments").append("<div class=\"dt-loader\"></div>");
},
success: function(response) {
if (response && response.success) {
alert_float('success', response.message || "<?= _l('appointment_deleted'); ?>");
// Force reload the table to remove the deleted appointment
$(".table-appointments").DataTable().ajax.reload(null, false);
}
},
complete: function() {
$(".table-appointments").find(".dt-loader").remove();
}
});
} else {
// Regular appointment delete
$.ajax({
url: admin_url + "appointly/appointments/delete/" + id,
type: 'POST',
dataType: 'json',
beforeSend: function() {
$(".table-appointments").append("<div class=\"dt-loader\"></div>");
},
success: function(response) {
if (response && response.success) {
alert_float('success', response.message || "<?= _l('appointment_deleted'); ?>");
$(".table-appointments").DataTable().ajax.reload(null, false);
}
},
complete: function() {
$(".table-appointments").find(".dt-loader").remove();
}
});
}
}
}
// Separate function specifically for Google synced appointments
function deleteGoogleSyncedAppointment(element) {
// Get the Google event ID from the data attribute
var googleEventId = $(element).data('google-event-id');
console.log('Google Event ID from data attribute:', googleEventId);
// If still no ID found, show error
if (!googleEventId || googleEventId === '') {
alert_float('danger', 'Could not find Google Calendar event ID');
return;
}
if (confirm("<?= _l('appointment_are_you_sure'); ?>\n\nThis will delete the event from Google Calendar directly.")) {
$.ajax({
url: admin_url + "appointly/appointments/deleteGoogleSyncedAppointment/" + googleEventId,
type: 'POST',
dataType: 'json',
beforeSend: function() {
$(".table-appointments").append("<div class=\"dt-loader\"></div>");
},
success: function(response) {
if (response && response.success) {
alert_float('success', response.message || "<?= _l('appointment_deleted'); ?>");
// Force reload the table to remove the deleted appointment
$(".table-appointments").DataTable().ajax.reload(null, false);
}
},
complete: function() {
$(".table-appointments").find(".dt-loader").remove();
}
});
}
}
/**
* Check if user is logged in to outlook
*
* @return boolean
*/
function isOutlookLoggedIn() {
if (typeof myMSALObj !== "undefined" && myMSALObj.getAccount()) {
return true;
}
return false;
}
// Mark appointment as finished
function markAppointmentAsFinished(id) {
$.post("appointments/finished", {
id: id,
beforeSend: function() {
$(".table-appointments").append("<div class=\"dt-loader\"></div>");
}
}).done(function(r) {
if (r.success == true) {
alert_float("success", appointly_lang_finished);
$(".table-appointments").DataTable().ajax.reload();
}
$(".table-appointments").find(".dt-loader").remove();
});
}
// Mark appointment as approved
function markAppointmentAsApproved(id) {
$.post("appointments/approve", {
appointment_id: id,
beforeSend: function() {
$(".table-appointments").append("<div class=\"dt-loader\"></div>");
}
}).done(function(r) {
r = JSON.parse(r);
if (r.result == true) {
alert_float("success", appointly_lang_approved);
$(".table-appointments").DataTable().ajax.reload();
}
$(".table-appointments").find(".dt-loader").remove();
}).fail(function(err) {
$("body").find(".dt-loader").remove();
console.log("An unknown error has been thrown" + err);
});;
}
// Mark appointment as cancelled
function markAppointmentAsCancelled(id) {
if (confirm(appointly_are_you_sure_mark_as_cancelled)) {
$.ajax({
url: "appointments/cancel_appointment",
type: "POST",
dataType: "json",
data: {
id: id
},
beforeSend: function() {
$(".table-appointments").append('<div class="dt-loader"></div>');
},
success: function(response) {
if (response.success === true) {
alert_float("success", appointly_lang_cancelled);
$(".table-appointments").DataTable().ajax.reload();
} else {
var message = response.message || "Cancellation failed";
alert_float("warning", message);
}
},
complete: function() {
$(".table-appointments").find(".dt-loader").remove();
}
});
}
}
// Mark appointment as ongoing if marked as cancelled
function markAppointmentAsOngoing(id) {
if (confirm(appointment_are_you_sure_mark_as_ongoing)) {
console.log("Starting mark as ongoing request for appointment ID:", id);
$.ajax({
url: "appointments/mark_as_ongoing_appointment",
type: "POST",
dataType: "json",
data: {
id: id
},
beforeSend: function() {
console.log("Sending request to mark appointment as ongoing...");
$(".table-appointments").append("<div class=\"dt-loader\"></div>");
},
success: function(response) {
console.log("Received response:", response);
if (response && response.success === true) {
alert_float("success", appointly_mark_as_ongoing);
} else {
var message = response && response.message ? response.message : "Status change failed";
alert_float("warning", message);
console.error("Error in markAppointmentAsOngoing:", response);
}
$(".table-appointments").DataTable().ajax.reload();
},
complete: function() {
console.log("Request completed");
$(".table-appointments").find(".dt-loader").remove();
}
});
}
}
/**
* Approve cancellation request
* @param {number} appointment_id - The appointment ID to approve cancellation for
*/
function approveCancellation(appointment_id) {
if (!appointment_id) {
alert_float('danger', 'Invalid appointment ID');
return;
}
if (!confirm("<?= _l('appointment_are_you_sure_to_cancel'); ?>")) {
return;
}
$.ajax({
url: admin_url + "appointly/appointments/cancel_appointment",
type: "POST",
dataType: "json",
data: {
id: appointment_id
},
beforeSend: function() {
$("body").append('<div class="dt-loader"></div>');
},
success: function(response) {
if (response.success === true) {
alert_float("success", "<?= _l('appointment_cancellation_approved'); ?>");
// Reload the page to reflect the changes
setTimeout(function() {
location.reload();
}, 1500);
} else {
var message = response.message || "Approval failed";
alert_float("warning", message);
}
},
complete: function() {
$("body").find(".dt-loader").remove();
}
});
}
// Mark appointment as no-show
function markAppointmentAsNoShow(id) {
if (confirm(appointly_are_you_sure_mark_as_no_show)) {
$.ajax({
url: "appointments/mark_as_no_show",
type: "POST",
dataType: "json",
data: {
id: id
},
beforeSend: function() {
$(".table-appointments").append('<div class="dt-loader"></div>');
},
success: function(response) {
if (response.success === true) {
alert_float("success", appointly_lang_no_show);
$(".table-appointments").DataTable().ajax.reload();
} else {
var message = response.message || "Status change failed";
alert_float("warning", message);
}
},
complete: function() {
console.log("Request completed");
$(".table-appointments").find(".dt-loader").remove();
$(".table-appointments").DataTable().ajax.reload();
}
});
}
}
</script>