Skip to content

Instantly share code, notes, and snippets.

@lucas-pelton
Last active October 6, 2017 22:53
Show Gist options
  • Save lucas-pelton/256e2c435a4d623d2670d1e672192bcb to your computer and use it in GitHub Desktop.
Save lucas-pelton/256e2c435a4d623d2670d1e672192bcb to your computer and use it in GitHub Desktop.
FullSlate front end API WP plugin
// http://jillix.github.io/jQuery-sidebar/
(function($){$.fn.sidebar=function(options){var self=this;if(self.length>1){return self.each(function(){$(this).sidebar(options)})}var width=self.outerWidth();var height=self.outerHeight();var settings=$.extend({speed:200,side:"left",isClosed:false,close:true},options);self.on("sidebar:open",function(ev,data){var properties={};properties[settings.side]=0;settings.isClosed=null;self.stop().animate(properties,$.extend({},settings,data).speed,function(){settings.isClosed=false;self.trigger("sidebar:opened")})});self.on("sidebar:close",function(ev,data){var properties={};if(settings.side==="left"||settings.side==="right"){properties[settings.side]=-self.outerWidth()}else{properties[settings.side]=-self.outerHeight()}settings.isClosed=null;self.stop().animate(properties,$.extend({},settings,data).speed,function(){settings.isClosed=true;self.trigger("sidebar:closed")})});self.on("sidebar:toggle",function(ev,data){if(settings.isClosed){self.trigger("sidebar:open",[data])}else{self.trigger("sidebar:close",[data])}});function closeWithNoAnimation(){self.trigger("sidebar:close",[{speed:0}])}if(!settings.isClosed&&settings.close){closeWithNoAnimation()}$(window).on("resize",function(){if(!settings.isClosed){return}closeWithNoAnimation()});self.data("sidebar",settings);return self};$.fn.sidebar.version="3.3.2"})(jQuery);
(function($){
$(".sidebar.right", '.off-canvas').sidebar({side: "right"});
if (window.location.hash) {
var user = JSON.parse(decodeURIComponent(window.location.hash).slice(1));
for (var key in user) {
jQuery('#'+key,"#customer-info").val(user[key]);
}
}
//SELECT SERVICE
$('.toggler').on('click',function(e){
$(".sidebar.right", '.off-canvas').trigger("sidebar:toggle");
});
$(document).keyup(function(e) {
if (e.keyCode == 27) { // escape key maps to keycode `27`
$(".sidebar.right", '.off-canvas').trigger("sidebar:close");
}
});
$('a[href*="fullslate.com"]', '#booking-sidebar').add('li', '#booking-sidebar').on('click',function(e){
e.stopPropagation();
e.preventDefault();
if ($(this).hasClass('selected') || $(this).closest('li').hasClass('selected'))
{
$('li:not(.selected)','#booking-services').slideDown();
$('.selected','#booking-services').removeClass('selected');
$('#response, #response-wrapper span').empty();
$('#show-more, #response, #customer-info', '#booking-form').slideUp();
}
else {
if ($(this).is('a')) {
var theService=$(this).attr('href').split('/').slice(-1);
// hide non-selected services
$(this).closest('li').add($(this).closest('ul').closest('li')).addClass('selected');
$('#booking-services').find('li:not(.selected)').slideUp();
// hide user form, in case they've already had a chance to open user form
$('label[class*="show-"]:not(.show-'+theService+'), #book-now, #customer-info, #show-more, #response ul').hide();
$('#response-wrapper span').html($(this).attr('title'));
// set form field value for ajax post
$('#service', '#booking-form').val(theService);
// fetch times for selected service
$('#show-soonest').click();
}
}
});
// NAVIGATE WEEKS
$('#show-soonest, #show-more').click(function(e,isAutoClick){
var $this = $(this),
ajaxurl = '/wp-admin/admin-ajax.php',
data = {
action: 'fullslate',
request: 'openings',
service: $('#service','#booking-form').val()
};
$('#response-wrapper').addClass('ajax');
if ("show-more" == $this.attr('id')) data.after=sundayMorning;
if ("show-more" == $this.attr('id') && !isAutoClick) data.slideDown=true
jQuery.post(ajaxurl, data, function(response){
var responseObj=JSON.parse(response),
sunday;
errorState=0,
theService=$('#service', '#booking-form').val();
if (responseObj.success!==true) {
/* Error handling ******
noResponse("FullSlate returned failure")
set errorState = 1 (for use by booking to know if we're sending a live request)
return
Live times unavailable now. What time works for you?
<input type="date" id="service-date" name="service-date" />
Retry for live times
************************/
console.log(responseObj);
}
var times=responseObj.openings;
$('#at', '#booking-form').val(''); //clear the hidden date field that is used to store selected appointment time
if (0 === times.length) sunday = comingSunday(new Date(),0); //get the first Sunday after today
else sunday = comingSunday(calenDate(times[times.length-1]),0); // get the first Sunday after the last available time
sundayMorning = new Date(sunday.setHours(0,0,1));
sundayMorning = sundayMorning.toISOString().replace(/[\-\.:]/g,"").replace(/\d{3}Z/g,"Z");
if (0 === times.length) { $('#show-more').trigger('click',[true]); return;}
$('#response-wrapper').removeClass('ajax');
if ($('#response ul').length) $('#response ul').slideUp('fast',function(theTimes=times){buildTimesUI(theTimes);});
else buildTimesUI(times);
true === data.slideDown ? $('#show-soonest').slideDown('fast') : $('#show-soonest').slideUp('fast');
$('#show-more, #response, .show-'+theService, '#booking-form').slideDown();
}).fail(function(){
alert('The request for openings was not able to be made');
/* Error handling ******
set errorState = 1 (for use by booking to know if we're sending a live request)
return
Live times unavailable now. What time works for you?
<input type="date" id="service-date" name="service-date" />
Retry for live times
************************/
});
});
// TOGGLE OPEN DAY
$('#response').on('click','.day',function(e) {
e.stopPropagation();
var $this=$(this);
if ($this.hasClass('selected')) {
$('h3 span',$this).remove();
$('li.selected',this).add($this).removeClass('selected');
$this.siblings().add('#show-more').slideDown('fast');
}
$('li',this).slideToggle('fast');
});
// SELECT TIME
$('#response').on('click','.day li',function(e){
e.stopPropagation();
var $this=$(this);
$('#at', '#booking-form').val($('span',this).attr('data-at'));
$('li, ul','#response').removeClass('selected');
$this.add($this.parent()).addClass('selected');
$('#response .day.selected h3').append('<span> &commat;'+$this.html()+'</span>');
$('#response').find('li,ul:not(.selected)').add('#show-more').slideUp();
$('#book-now, #customer-info').slideDown('fast');
});
// BOOK APPOINTMENT
$('#book-now').click(function(){
var now=Date.now();
var data = {
action: 'fullslate',
request: 'book',
at: $('#at','#booking-form').val(),
service: $('#service','#booking-form').val(),
first_name: $('#first_name','#booking-form').val(),
last_name: $('#last_name','#booking-form').val(),
email: $('#email','#booking-form').val(),
phone_number: $('#phone_number','#booking-form').val()
},
ajaxurl = '/wp-admin/admin-ajax.php';
if ($('.more-info, label.show-'+data.service,'#booking-form').length) {
data['custom-notes'] = "";
$('.more-info, label.show-'+data.service,'#booking-form').each(function(){
var $input=$('input',this);
data['custom-notes']+=$input.attr('name').replace(/_/g," ")+": "+$input.val()+"|";
});
}
if (0 === errorState){ // have we set a local error anywhere along the way?
/*jQuery.post(ajaxurl, data, function(response){
var responseObj=JSON.parse(response);
if (true === responseObj.failure ) console.log(responseObj); //handle error
});*/
//console.log (data);
jQuery.post('https://hook.integromat.com/lslfodxrp0kbt7kxyek66bd8ceqk2q8n', data, function(response){
console.log(response);
console.log((Date.now()-now)/1000 + " Sec.");
var responseObj=JSON.parse(response);
console.log(responseObj); //handle error
});
}
else {}//send someone an email saying appointment request api endpoint broken
});
function buildTimesUI (times){
$('#response').empty();
var adjustedTimes=[],
i=0;
times.forEach(function(time){
var openingTime=calenDate(time);
openingTime.setHours(openingTime.getHours() - openingTime.getTimezoneOffset() / 60);
adjustedTimes.push(openingTime.format('fullSlateDateTime'));
});
var openingsObj = groupByArray(adjustedTimes,function(o){return o.substring(0,8);});
openingsObj.forEach(function(day){
var titleDate=calenDate(day.key+"T000000Z");
$('#response').append('<ul class="day"><h3>'+titleDate.format("ddd. mmm dd")+'</h3></ul>');
var $today=$('.day','#response').last();
day.values.forEach(function(time){
var localTime=calenDate(time),
fsTime=calenDate(time);
fsTime.setHours(fsTime.getHours() + fsTime.getTimezoneOffset() / 60);
$today.append('<li><span data-at="'+fsTime.format("fullSlateDateTime")+'">'+localTime.format("shortTime")+'</span></li>');
});
});
$('#response ul').slideDown('slow');
}
function groupByArray(xs, key) {
return xs.reduce(function (rv, x) {
let v = key instanceof Function ? key(x) : x[key];
let el = rv.find((r) => r && r.key === v);
if (el) {
el.values.push(x);
}
else {
rv.push({
key: v,
values: [x]
});
}
return rv;
}, []);
}
function comingSunday(d, dow){
d.setDate(d.getDate() + (dow+(7-d.getDay())) % 7);
return d;
}
function calenDate(icalStr) {
var strYear = icalStr.substr(0,4);
var strMonth = parseInt(icalStr.substr(4,2),10)-1;
var strDay = icalStr.substr(6,2);
var strHour = icalStr.substr(9,2);
var strMin = icalStr.substr(11,2);
var myDate = new Date(strYear,strMonth, strDay, strHour, strMin);
return myDate;
}
/*
* Date Format 1.2.3
* (c) 2007-2009 Steven Levithan <stevenlevithan.com>
* MIT license
*
* Includes enhancements by Scott Trenda <scott.trenda.net>
* and Kris Kowal <cixar.com/~kris.kowal/>
*
* Accepts a date, a mask, or a date and a mask.
* Returns a formatted version of the given date.
* The date defaults to the current date/time.
* The mask defaults to dateFormat.masks.default.
*/
var dateFormat = function () {
var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
timezoneClip = /[^-+\dA-Z]/g,
pad = function (val, len) {
val = String(val);
len = len || 2;
while (val.length < len) val = "0" + val;
return val;
};
// Regexes and supporting functions are cached through closure
return function (date, mask, utc) {
var dF = dateFormat;
// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
mask = date;
date = undefined;
}
// Passing date through Date applies Date.parse, if necessary
date = date ? new Date(date) : new Date;
if (isNaN(date)) throw SyntaxError("invalid date");
mask = String(dF.masks[mask] || mask || dF.masks["default"]);
// Allow setting the utc argument via the mask
if (mask.slice(0, 4) == "UTC:") {
mask = mask.slice(4);
utc = true;
}
var _ = utc ? "getUTC" : "get",
d = date[_ + "Date"](),
D = date[_ + "Day"](),
m = date[_ + "Month"](),
y = date[_ + "FullYear"](),
H = date[_ + "Hours"](),
M = date[_ + "Minutes"](),
s = date[_ + "Seconds"](),
L = date[_ + "Milliseconds"](),
o = utc ? 0 : date.getTimezoneOffset(),
flags = {
d: d,
dd: pad(d),
ddd: dF.i18n.dayNames[D],
dddd: dF.i18n.dayNames[D + 7],
m: m + 1,
mm: pad(m + 1),
mmm: dF.i18n.monthNames[m],
mmmm: dF.i18n.monthNames[m + 12],
yy: String(y).slice(2),
yyyy: y,
h: H % 12 || 12,
hh: pad(H % 12 || 12),
H: H,
HH: pad(H),
M: M,
MM: pad(M),
s: s,
ss: pad(s),
l: pad(L, 3),
L: pad(L > 99 ? Math.round(L / 10) : L),
t: H < 12 ? "a" : "p",
tt: H < 12 ? "am" : "pm",
T: H < 12 ? "A" : "P",
TT: H < 12 ? "AM" : "PM",
Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
};
return mask.replace(token, function ($0) {
return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
});
};
}();
// Some common format strings
dateFormat.masks = {
"default": "ddd mmm dd yyyy HH:MM:ss",
shortDate: "m/d/yy",
mediumDate: "mmm d, yyyy",
longDate: "mmmm d, yyyy",
fullDate: "dddd, mmmm d, yyyy",
shortTime: "h:MM TT",
mediumTime: "h:MM:ss TT",
longTime: "h:MM:ss TT Z",
isoDate: "yyyy-mm-dd",
isoTime: "HH:MM:ss",
isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'",
fullSlateDateTime: "yyyymmdd'T'HHMMss'Z'"
};
// Internationalization strings
dateFormat.i18n = {
dayNames: [
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
],
monthNames: [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
]
};
// For convenience...
Date.prototype.format = function (mask, utc) {
return dateFormat(this, mask, utc);
};
})(jQuery);
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions, and defines a function
* that starts the plugin.
*
* @link https://lucasbalzer.com
* @since 1.0.0
* @package Wp_Full_Slate
*
* @wordpress-plugin
* Plugin Name: Full Slate
* Plugin URI: https://lucasbalzer.com
* Description: This is a short description of what the plugin does. It's displayed in the WordPress admin area.
* Version: 1.0.0
* Author: Lucas Balzer
* Author URI: https://lucasbalzer.com
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: full-slate
* Domain Path: /languages
*/
/**
* Send a POST requst using cURL
* @param string $url to request
* @param array $post values to send
* @param array $options for cURL
* @return string
*/
function fs_post($url, array $post = NULL, array $options = array())
{
$defaults = array(
CURLOPT_POST => 1,
CURLOPT_HEADER => 0,
CURLOPT_URL => $url,
CURLOPT_FRESH_CONNECT => 1,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FORBID_REUSE => 1,
CURLOPT_TIMEOUT => 4,
CURLOPT_POSTFIELDS => http_build_query($post)
);
$ch = curl_init();
curl_setopt_array($ch, ($options + $defaults));
if( ! $result = curl_exec($ch))
{
trigger_error(curl_error($ch));
}
curl_close($ch);
return $result;
}
function get_openings() {
$params=array(
'service'=>$_POST['service'],
'window' => 'week'
);
if(isset($_POST['after'])) $params['after'] = $_POST['after'];
echo fs_post("https://XXX.fullslate.com/api/openings",$params);
}
function book () {
$params=array(
'at'=>$_POST['at'],
'service'=>$_POST['service'],
'first_name'=>$_POST['first_name'],
'last_name'=>$_POST['last_name'],
'email'=>$_POST['email'],
'phone_number'=>$_POST['phone_number']/*,
'custom-notes' => $_POST['custom-notes']*/
);
$result=fs_post("https://XXX.fullslate.com/api/bookings/",$params);
// JSON Parse $result to check for success/failure
// if (success) ping webhook endpoint with data
echo $result;
}
function fullslate(){
switch ($_POST['request']) {
case 'openings': get_openings(); break;
case 'book': book(); break;
}
wp_die();
}
add_action('wp_ajax_fullslate', 'fullslate' ); // executed when logged in
add_action('wp_ajax_nopriv_fullslate', 'fullslate' ); // executed when logged out
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment