jQuery(function($){
'use strict';
var nonce=absPublic.nonce;
var ajaxUrl=absPublic.ajax_url;
$(document).on('mousedown', 'button', function(){
var $b=$(this);
if(!$b.data('abs-original-html')) $b.data('abs-original-html', $b.html());
});
$(document).ajaxError(function(_e, xhr, settings){
if(!settings||typeof settings.url!=='string'||settings.url.indexOf('admin-ajax.php')===-1) return;
var hint=xhr.status ? ('HTTP ' + xhr.status):'network error';
console.error('[ABS public] AJAX error', hint, (xhr.responseText||'').substring(0, 240));
var $stuck=$('button:disabled').filter(function(){
var t=(($(this).text()||'') + ' ' + ($(this).html()||'')).toLowerCase();
return /confirming|loading|signing|creating|cancelling|rescheduling|…|\.\.\./i.test(t);
});
$stuck.each(function(){
var $b=$(this);
var orig=$b.data('abs-original-html');
if(orig) $b.html(orig);
$b.prop('disabled', false);
});
});
function absAjax(action, data, onSuccess, onError){
var payload=$.extend({ action: action, nonce: nonce }, data||{});
return $.post(ajaxUrl, payload)
.done(function(res){
if(res&&res.success===true){
if(typeof onSuccess==='function') onSuccess(res.data);
}else{
var msg=(res&&res.data&&res.data.message) ? res.data.message
: (res&&typeof res.data==='string') ? res.data
: 'Operation failed.';
if(typeof onError==='function') onError(msg, null);
}})
.fail(function(xhr){
var status=xhr&&xhr.status ? xhr.status:0;
var msg='Connection error' + (status ? ' (HTTP ' + status + ')':'') + '. Please try again.';
if(typeof onError==='function') onError(msg, xhr);
});
}
window.absAjax=absAjax;
var c=absPublic.colors||{};
var r=document.documentElement;
function setVar(name, val){
if(typeof val==='string'&&/^#[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$/.test(val)){
r.style.setProperty(name, val);
}}
setVar('--abs-primary',    c.primary);
setVar('--abs-secondary',  c.secondary);
setVar('--abs-accent',     c.accent);
setVar('--abs-btn-text',   c.button_text);
setVar('--abs-form-bg',    c.form_bg);
setVar('--abs-form-text',  c.form_text);
var today=new Date().toISOString().split('T')[0];
$('#abs-date-input').attr('min', today);
$(document).on('change', '#abs-service-select', function(){
var svcId=$(this).val();
$('#abs-time-slots').html('<div class="abs-no-slots">Select a date to see available slots.</div>');
$('#abs-selected-slot').val('');
if(svcId){
$.post(ajaxUrl, { action:'abs_get_staff_for_service', nonce:nonce, service_id:svcId }, function(res){
var $staffWrap=$('#abs-staff-field-wrap');
if(res.success&&res.data.length){
var opts='<option value="">— Any available —</option>';
$.each(res.data, function(i, s){
opts +='<option value="'+s.id+'">'+s.name+(s.position?' – '+s.position:'')+'</option>';
});
$staffWrap.find('select').html(opts);
$staffWrap.show();
}else{
$staffWrap.hide();
}}).fail(function(){
$('#abs-staff-field-wrap').hide();
});
}
if(svcId&&$('#abs-date-input').val()) loadSlots();
});
$(document).on('change', '#abs-date-input', function(){
if($('#abs-service-select').val()) loadSlots();
});
$(document).on('change', '#abs-staff-select', function(){
if($('#abs-service-select').val()&&$('#abs-date-input').val()) loadSlots();
});
function loadSlots(){
var svcId=$('#abs-service-select').val();
var date=$('#abs-date-input').val();
var staffId=$('#abs-staff-select').val()||0;
$('#abs-time-slots').html('<div class="abs-loading"><span class="abs-spinner" style="border-color:rgba(37,99,235,.3);border-top-color:'+c.primary+'"></span> Loading available times…</div>');
$('#abs-selected-slot').val('');
$.post(ajaxUrl, {
action:'abs_get_slots', nonce:nonce,
service_id:svcId, date:date, staff_id:staffId
}, function(res){
if(!res.success){
$('#abs-time-slots').html('<div class="abs-no-slots">⚠️ Could not load time slots. Please try again.</div>');
return;
}
if(!res.data.length){
$('#abs-time-slots').html('<div class="abs-no-slots">😔 No available slots on this date. Please try another day.</div>');
return;
}
var html='<div class="abs-slots-grid">';
$.each(res.data, function(i, slot){
html +='<button type="button" class="abs-slot-btn" data-start="'+slot.start+'" data-end="'+slot.end+'">'+slot.label+'</button>';
});
html +='</div>';
$('#abs-time-slots').html(html);
}).fail(function(){
$('#abs-time-slots').html('<div class="abs-no-slots">Connection error loading slots.</div>');
});
}
$(document).on('click', '.abs-slot-btn:not(.unavailable)', function(){
$('.abs-slot-btn').removeClass('active');
$(this).addClass('active');
$('#abs-selected-slot').val($(this).data('start'));
$('#abs-selected-slot-end').val($(this).data('end'));
var $section=$('#abs-customer-section');
if($section.length){
$('html,body').animate({ scrollTop: $section.offset().top - 20 }, 400);
}});
$(document).on('click', '#abs-submit-booking', function(){
var $btn=$(this);
var $form=$('#abs-booking-form');
$('#abs-booking-error').remove();
var valid=true;
$form.find('[required]').each(function(){
$(this).css('border-color','');
if(!$(this).val().trim()){
$(this).css('border-color','#ef4444');
valid=false;
}});
if($('#abs-meeting-location').length&&!$('#abs-meeting-location').val()){
$('#abs-location-error').show();
showError('Please select a meeting location.');
$('html,body').animate({ scrollTop: $('#abs-location-grid').offset().top - 20 }, 300);
return;
}
if(!$('#abs-selected-slot').val()){
showError('Please select an available time slot.');
return;
}
if(!valid){
showError('Please fill in all required fields.');
$form.find('[required]').filter(function(){ return !$(this).val().trim(); }).first().focus();
return;
}
$btn.prop('disabled', true).html('<span class="abs-spinner"></span>Confirming…');
var data={ action:'abs_submit_booking', nonce:nonce };
$form.find('input,select,textarea').each(function(){
var name=$(this).attr('name')||$(this).attr('id');
if(name) data[name]=$(this).val();
});
data['start_time']=$('#abs-selected-slot').val();
$.post(ajaxUrl, data, function(res){
$btn.prop('disabled', false).html('✓ Confirm Booking');
if(res.success){
var d=res.data;
if(d.needs_payment&&d.payment_url){
var html='<div class="abs-success-msg" style="background:#fffbeb;border-color:#fde68a;color:#92400e">'
+ '<div class="abs-success-icon">📋</div>'
+ '<h3>Almost Done!</h3>'
+ '<p>Your booking reference is: <strong class="abs-success-ref">'+d.booking_ref+'</strong></p>'
+ '<p>You will now be redirected to complete payment.</p>'
+ '<div style="margin-top:16px"><a href="'+d.payment_url+'" class="abs-submit-btn" style="display:inline-block;text-decoration:none;padding:12px 28px;max-width:280px">💳 Proceed to Payment →</a></div>'
+ '</div>';
$('#abs-booking-form-wrap').html(html);
setTimeout(function(){ window.location.href=d.payment_url; }, 2500);
}else{
var html2='<div class="abs-success-msg">'
+ '<div class="abs-success-icon">🎉</div>'
+ '<h3>Booking Confirmed!</h3>'
+ '<p>Your booking reference is:</p>'
+ '<div class="abs-success-ref">'+d.booking_ref+'</div>'
+ '<p style="margin-top:12px;font-size:13px;color:#64748b">A confirmation email has been sent to you.</p>'
+ (d.ical_url ? '<a href="'+d.ical_url+'" style="display:inline-block;margin-top:12px;padding:9px 18px;background:#f1f5f9;color:#1e293b;border-radius:8px;text-decoration:none;font-size:13px;font-weight:600">📅 Add to Calendar (.ics)</a>':'')
+ '</div>';
$('#abs-booking-form-wrap').html(html2);
}}else{
var emsg=(res.data&&typeof res.data==='object') ? (res.data.message||'Something went wrong. Please try again.'):(res.data||'Something went wrong. Please try again.');
showError(emsg);
$btn.html('✓ Confirm Booking');
}}).fail(function(){
$btn.prop('disabled', false).html('✓ Confirm Booking');
showError('Connection error. Please check your internet and try again.');
});
});
function showError(msg){
$('#abs-booking-error').remove();
$('<div id="abs-booking-error" class="abs-error-msg">⚠️ '+msg+'</div>').insertBefore('#abs-submit-booking');
$('html,body').animate({ scrollTop: $('#abs-booking-error').offset().top - 20 }, 300);
}
$(document).on('input change', '#abs-booking-form [required]', function(){
$(this).css('border-color','');
});
$(document).on('click', '.abs-portal-tab-btn', function(){
var tab=$(this).data('tab');
$('.abs-portal-tab-btn').removeClass('active').css({'background':'transparent','color':c.primary,'border':'2px solid '+c.primary});
$(this).addClass('active').css({'background':c.primary,'color':'#fff','border':'2px solid '+c.primary});
$('.abs-portal-tab-panel').hide();
$('#abs-portal-tab-'+tab).show();
});
$(document).on('click', '#abs-portal-login-btn', function(){
var email=$('#abs-portal-email').val();
var pass=$('#abs-portal-pass').val();
if(!email||!pass){ portalMsg('Please enter your email and password.','error'); return; }
var $btn=$(this).prop('disabled',true).text('Signing in…');
$.post(ajaxUrl, {action:'abs_customer_login',nonce:nonce,email:email,password:pass}, function(res){
$btn.prop('disabled',false).text('Sign In');
if(res.success) location.reload();
else portalMsg((res.data&&res.data.message) ? res.data.message:'Sign in failed.','error');
}).fail(function(xhr){
$btn.prop('disabled',false).text('Sign In');
portalMsg('Connection error' + (xhr.status ? ' (' + xhr.status + ')':'') + '. Please try again.','error');
});
});
$(document).on('keydown','#abs-portal-pass',function(e){ if(e.key==='Enter') $('#abs-portal-login-btn').trigger('click'); });
$(document).on('click','#abs-portal-register-btn',function(){
var name=$('#abs-reg-name').val(),email=$('#abs-reg-email').val(),pass=$('#abs-reg-pass').val(),phone=$('#abs-reg-phone').val();
if(!name||!email||!pass){portalMsg('Please fill in all required fields.','error');return;}
var $btn=$(this).prop('disabled',true).text('Creating account…');
$.post(ajaxUrl,{action:'abs_customer_register',nonce:nonce,name:name,email:email,password:pass,phone:phone},function(res){
$btn.prop('disabled',false).text('Create Account');
if(res.success) location.reload();
else portalMsg((res.data&&res.data.message) ? res.data.message:'Registration failed.','error');
}).fail(function(xhr){
$btn.prop('disabled',false).text('Create Account');
portalMsg('Connection error' + (xhr.status ? ' (' + xhr.status + ')':'') + '. Please try again.','error');
});
});
$(document).on('click','#abs-portal-logout-btn',function(){
$.post(ajaxUrl,{action:'abs_customer_logout',nonce:nonce},function(){ location.reload(); })
.fail(function(){ location.reload(); });
});
$(document).on('click','#abs-forgot-link',function(e){
e.preventDefault();
$('.abs-portal-tab-panel').hide();
$('#abs-portal-tab-forgot').show();
});
$(document).on('click','#abs-forgot-back-link',function(e){
e.preventDefault();
$('.abs-portal-tab-panel').hide();
$('#abs-portal-tab-login').show();
});
$(document).on('click','#abs-forgot-submit-btn',function(){
var email=$('#abs-forgot-email').val();
if(!email){ portalMsg('Please enter your email.','error'); return; }
var $btn=$(this).prop('disabled',true).text('Sending…');
$.post(ajaxUrl,{action:'abs_request_password_reset',nonce:nonce,email:email},function(res){
$btn.prop('disabled',false).text('Send reset link');
portalMsg((res.data&&res.data.message) ? res.data.message:'If an account exists for that email, a reset link has been sent.','success');
$('#abs-forgot-email').val('');
}).fail(function(xhr){
$btn.prop('disabled',false).text('Send reset link');
portalMsg('Connection error' + (xhr.status ? ' (' + xhr.status + ')':'') + '. Please try again.','error');
});
});
$(document).on('click','#abs-reset-submit-btn',function(){
var token=$('#abs-reset-token').val();
var email=$('#abs-reset-email').val();
var pass=$('#abs-reset-pass').val();
var confirm=$('#abs-reset-pass-confirm').val();
var $msg=$('#abs-reset-msg');
if(!pass||pass.length < 8){ $msg.html('<div class="abs-error-msg">Password must be at least 8 characters.</div>'); return; }
if(pass!==confirm){ $msg.html('<div class="abs-error-msg">Passwords do not match.</div>'); return; }
var $btn=$(this).prop('disabled',true).text('Saving…');
$.post(ajaxUrl,{action:'abs_complete_password_reset',nonce:nonce,token:token,email:email,new_password:pass},function(res){
$btn.prop('disabled',false).text('Set new password');
if(res.success){
$msg.html('<div class="abs-success-msg">' + ((res.data&&res.data.message)||'Password reset.') + ' Redirecting to sign in…</div>');
setTimeout(function(){ location.href=location.pathname; }, 1500);
}else{
$msg.html('<div class="abs-error-msg">' + ((res.data&&res.data.message)||'Could not reset password.') + '</div>');
}}).fail(function(xhr){
$btn.prop('disabled',false).text('Set new password');
$msg.html('<div class="abs-error-msg">Connection error' + (xhr.status ? ' (' + xhr.status + ')':'') + '. Please try again.</div>');
});
});
function portalMsg(msg,type){
var cls=type==='error'?'abs-error-msg':'abs-success-msg';
$('#abs-portal-msg').html('<div class="'+cls+'">'+msg+'</div>');
setTimeout(function(){$('#abs-portal-msg').html('');},4000);
}
$(document).on('click keypress', '.abs-location-card', function(e){
if(e.type==='keypress'&&e.which!==13) return;
$('.abs-location-card').removeClass('selected');
$(this).addClass('selected');
var val=$(this).data('value');
$('#abs-meeting-location').val(val);
$('#abs-location-error').hide();
});
});