- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-10-2022 03:34 PM
Im trying to customize the UI for the Walkup Experience, Appointment Booking Widget. Ive been able to show the window on the button by editing the HTML, however id also like to display the number of available slots for each window. i cant figure out how to get this information. has anyone done this???
this is what it looks like now:
Id like it to look something like this:
Solved! Go to Solution.
- Labels:
-
Service Portal
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-11-2022 07:05 AM
This took a while to figure out but was actually pretty simple...
I edited the Widget Appointment Booking
For Server Script:
so first i had to grab from the config the number of appts allowed per time slot and pass to the data variable to access within client script (data.bookingSlotsTotal). I added these two scripts to the top of the server script area around line 12
//get max number of appts per window from config table, table has only 1 record, so query and grab the data
var bookingSlots = new GlideRecord('sn_apptmnt_booking_service_config');
bookingSlots.query();
while(bookingSlots.next()){
data.bookingSlotsTotal = parseInt(bookingSlots.appointments_per_bookable_slot);
}
Then i had to query the appointment table to grab a list of all the appts made, i dont care who or where or what it is for, just care about the actual slots, so i built an array of these slots and passed them to the client script throught data.apps. I added this at around line 19
//get an array of the appt slots currently taken for current location id from url, pass to client script so they can be evaluated against the calendar.
var location_id = $sp.getParameter('location_id'); //grab location from URL
data.appts = []; //start my array
var queryText = 'stateNOT IN3,4,7^window_startRELATIVEGT@minute@ago@0^wu_location_queue='+location_id; //grabs all records that are not cancelled, Closed Complete or Closed Incomplete, that the start date is in the future, and the location is for the location from URL.
var avail = new GlideRecord('wu_appointment');
avail.addEncodedQuery(queryText);
avail.query();
while(avail.next()){
var apptsArr = "";
apptsArr = avail.getDisplayValue('window_start');
data.appts.push(apptsArr); //push the value into my declared array and have access to it in client since its a data object.
}
For Client Script:
i added the following script to the top of the client script around line 10. This grabs the data from the server script to use lower in the client script.
//grab the array of appts scheduled from server script
var appts = c.data.appts;
//gets the number of appts currently booked for the available booking window by looking at the length of the booked appts array
var length = appts.length;
//gets the total booking slots allowed from server script
var totalApptsAllowed = c.data.bookingSlotsTotal;
at around line 350 youll see the actualWindow being declared from window values this is the bottom of the main client script, below here are supporting functions to help work with the data, here, above the //TODO which categorizes the slots into morning, day and night categories, i created a new value called actualWindow.slots which i will be referencing in my HTML. I created two functions to evaluate the date selected vs the dates being passed for selected appointments.
actualWindow.slots = findAppt(reformatDate(window.start_date),appts);
my two functions go right below the closure of the main client script at around line 358
This first function reformatDate, reformats the order of the date field so that when i go to see if the appointment slot matches a booked appointment, they are the same format:
function reformatDate(date){
var dateTimePieces = date.split(" ");
var datePiece = dateTimePieces[0];
var dateSplit = datePiece.split("-");
var newDate = dateSplit[1]+"-"+dateSplit[2]+"-"+dateSplit[0]+" "+dateTimePieces[1];
newDate = newDate.slice(0,newDate.length-3);
// alert(newDate);
return newDate;
}
This second function actually performs the comparison using the reformatted date above:
it sets a counter to 0, then ensures theres appointments booked within the window, if theres none, it skips the comparison since its not needed and returns the number of available slots for each window
function findAppt(date, apptsObj){
var count = 0
if(length > 0){
for(var aa=0; aa < length; aa++){
if(apptsObj[aa] == date){
count++
}
}
}
var availAppts = totalApptsAllowed - count;
return availAppts;
}
now that i have my slots i need to put them into my html code. i see two spots for these at the time of this posting, one for mobile and one for regular.
your looking for the div class="content" block to inject your variables.
where you see the {{appointmentWindow.start}} variables (i see them 2x in this block) i added
({{appointmentWindow.slots}})
which will show (2) next to the start time. or whatever number of slots are available.
if you are able to follow this, then it should be working!
let me know below how it fared for you!!!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-11-2022 07:05 AM
This took a while to figure out but was actually pretty simple...
I edited the Widget Appointment Booking
For Server Script:
so first i had to grab from the config the number of appts allowed per time slot and pass to the data variable to access within client script (data.bookingSlotsTotal). I added these two scripts to the top of the server script area around line 12
//get max number of appts per window from config table, table has only 1 record, so query and grab the data
var bookingSlots = new GlideRecord('sn_apptmnt_booking_service_config');
bookingSlots.query();
while(bookingSlots.next()){
data.bookingSlotsTotal = parseInt(bookingSlots.appointments_per_bookable_slot);
}
Then i had to query the appointment table to grab a list of all the appts made, i dont care who or where or what it is for, just care about the actual slots, so i built an array of these slots and passed them to the client script throught data.apps. I added this at around line 19
//get an array of the appt slots currently taken for current location id from url, pass to client script so they can be evaluated against the calendar.
var location_id = $sp.getParameter('location_id'); //grab location from URL
data.appts = []; //start my array
var queryText = 'stateNOT IN3,4,7^window_startRELATIVEGT@minute@ago@0^wu_location_queue='+location_id; //grabs all records that are not cancelled, Closed Complete or Closed Incomplete, that the start date is in the future, and the location is for the location from URL.
var avail = new GlideRecord('wu_appointment');
avail.addEncodedQuery(queryText);
avail.query();
while(avail.next()){
var apptsArr = "";
apptsArr = avail.getDisplayValue('window_start');
data.appts.push(apptsArr); //push the value into my declared array and have access to it in client since its a data object.
}
For Client Script:
i added the following script to the top of the client script around line 10. This grabs the data from the server script to use lower in the client script.
//grab the array of appts scheduled from server script
var appts = c.data.appts;
//gets the number of appts currently booked for the available booking window by looking at the length of the booked appts array
var length = appts.length;
//gets the total booking slots allowed from server script
var totalApptsAllowed = c.data.bookingSlotsTotal;
at around line 350 youll see the actualWindow being declared from window values this is the bottom of the main client script, below here are supporting functions to help work with the data, here, above the //TODO which categorizes the slots into morning, day and night categories, i created a new value called actualWindow.slots which i will be referencing in my HTML. I created two functions to evaluate the date selected vs the dates being passed for selected appointments.
actualWindow.slots = findAppt(reformatDate(window.start_date),appts);
my two functions go right below the closure of the main client script at around line 358
This first function reformatDate, reformats the order of the date field so that when i go to see if the appointment slot matches a booked appointment, they are the same format:
function reformatDate(date){
var dateTimePieces = date.split(" ");
var datePiece = dateTimePieces[0];
var dateSplit = datePiece.split("-");
var newDate = dateSplit[1]+"-"+dateSplit[2]+"-"+dateSplit[0]+" "+dateTimePieces[1];
newDate = newDate.slice(0,newDate.length-3);
// alert(newDate);
return newDate;
}
This second function actually performs the comparison using the reformatted date above:
it sets a counter to 0, then ensures theres appointments booked within the window, if theres none, it skips the comparison since its not needed and returns the number of available slots for each window
function findAppt(date, apptsObj){
var count = 0
if(length > 0){
for(var aa=0; aa < length; aa++){
if(apptsObj[aa] == date){
count++
}
}
}
var availAppts = totalApptsAllowed - count;
return availAppts;
}
now that i have my slots i need to put them into my html code. i see two spots for these at the time of this posting, one for mobile and one for regular.
your looking for the div class="content" block to inject your variables.
where you see the {{appointmentWindow.start}} variables (i see them 2x in this block) i added
({{appointmentWindow.slots}})
which will show (2) next to the start time. or whatever number of slots are available.
if you are able to follow this, then it should be working!
let me know below how it fared for you!!!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-18-2022 09:22 AM
Ok so my requirement changed, we are now using multiple different time slots per location so i had to start over from scratch since my original method only allowed one set of window times per location, this new method lets you setup for a location between these dates/times to have its specific window and slots per window, you can grow this to many different slots per day/week/month/season etc...
the result will look something like this:
to allow for this, the location needs to have the enable day level configuration activated...
you can do this by going to Appointment Booking -> Appointment Booking Configuration - Walk-up Experience and from the related list Appointment Booking Service Configuration, add/edit a location.
check the box for Enable Day level configuration (if you dont see this, ensure your view is set to default).
Once enabled a new related list will display allowing you to setup the custom schedule you want to create. (note, my script does not use the bookable days function as of yet, if i add this functionality, i will add a new post here)
Widget: Appointment Booking
For server script i adjusted the original code to allow for the original intent or the new (this is to replace the code originally indicated it should be inserted around line 12:
//get max number of appts per window from config table, table has only 1 record, so query and grab the data
var bookingSlots = new GlideRecord('wu_location_queue');
bookingSlots.addQuery('sys_id', location_id);
bookingSlots.query();
while(bookingSlots.next()){
data.daySchedule = bookingSlots.appointment_booking.enable_day_level_config;
//if no day schedule is enabled just take the one slot for the location
if(!data.daySchedule){
data.bookingSlotsTotal = parseInt(bookingSlots.appointment_booking.appointments_per_bookable_slot);
}
//if a day schedule has been configured grab it and store each record as a JSON within an array to be passed to client to be evaluated
else{
data.bookingSchedule = [];
data.dayLevelSysId = bookingSlots.appointment_booking.sys_id;
var gr = new GlideRecord('sn_apptmnt_booking_day_configuration');
gr.addQuery('service_configuration', data.dayLevelSysId);
gr.addQuery('active',true);
gr.query();
var j = 0;
var str2 = "{";
var length = gr.getRowCount();
while(gr.next()){
var str = {"name" : gr.name.getDisplayValue(), "start_date" : gr.start_date.getDisplayValue(), "end_date" : gr.end_date.getDisplayValue(), "daily_start_time": gr.daily_start_time.getDisplayValue(), "daily_end_time" : gr.daily_end_time.getDisplayValue(), "appointments_per_bookable_slot" : gr.appointments_per_bookable_slot.getDisplayValue()};
var parser = new global.JSON();
var JSONObject = parser.encode(str);
data.bookingSchedule.push(JSONObject);
}
data.bookingSlotsTotal = "";
}
}
Adjust the client script:
for the entry around line 10, change it to:
var appts = c.data.appts;
var availAppts ;
var length = appts.length;
var dayLevelID = c.data.dayLevelSysId;
var totalApptsAllowed = c.data.bookingSlotsTotal;
var bookingSchedule = c.data.bookingSchedule;
around line 350 update the actualWindow.slots to be:
actualWindow.slots = findAppt(reformatDate(window.start_date),appts, actualWindow.start);
replace the findAppt function with my new function:
function findAppt(date, apptsObj, startTime){
var count = 0
if(length > 0){
for(var aa=0; aa < length; aa++){
if(apptsObj[aa] == date){
count++
}}}
if(!c.data.daySchedule){
availAppts = totalApptsAllowed - count;}
else{
availAppts = 10;
var lookupsysid = c.data.dayLevelSysId;
var windowD = date.split(" ");
var windowDate = windowD[0];
var a = 0
while(a<bookingSchedule.length){
var value = bookingSchedule[a];
var test = JSON.parse(value);
if((windowDate < test.end_date && windowDate > test.start_date)){
if(test.daily_start_time <= startTime && test.daily_end_time >= startTime)
availAppts = test.appointments_per_bookable_slot - count;
}
a++;
}
}
return availAppts;
}
For the html, i wanted to see the window start/end times so i modified the appointmentWindow.start from my original post where i see div class="content" to:
{{appointmentWindow.start}} - {{appointmentWindow.end}}<br>{{appointmentWindow.slots}} Available
good luck, and if anyone takes the time to develop the day of the week level configuration let me know, otherwise itll be my next stab at this project.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-18-2022 10:09 AM - edited 10-18-2022 10:11 AM
Ok i got the day of the week level configuration to work...
For Server Script i had to add the day of the week to the JSON object,
replace:
var str = {"name" : gr.name.getDisplayValue(), "start_date" : gr.start_date.getDisplayValue(), "end_date" : gr.end_date.getDisplayValue(), "daily_start_time": gr.daily_start_time.getDisplayValue(), "daily_end_time" : gr.daily_end_time.getDisplayValue(), "appointments_per_bookable_slot" : gr.appointments_per_bookable_slot.getDisplayValue()};
with:
var str = {"name" : gr.name.getDisplayValue(), "start_date" : gr.start_date.getDisplayValue(), "end_date" : gr.end_date.getDisplayValue(), "daily_start_time": gr.daily_start_time.getDisplayValue(), "daily_end_time" : gr.daily_end_time.getDisplayValue(), "appointments_per_bookable_slot" : gr.appointments_per_bookable_slot.getDisplayValue(), "bookable_days" : gr.bookable_days.getDisplayValue()};
then in client script replace:
actualWindow.slots = findAppt(reformatDate(window.start_date),appts, actualWindow.start);
with:
actualWindow.slots = findAppt(reformatDate(window.start_date),appts, actualWindow.start, actualWindow.dayName);
and finally
replace the function findAppt with:
function findAppt(date, apptsObj, startTime, dayName){
var count = 0
if(length > 0){
for(var aa=0; aa < length; aa++){
if(apptsObj[aa] == date){
count++
}}}
var dayInt;
if (dayName == "Monday") {dayInt = "1";}
else if (dayName == "Tuesday") {dayInt = "2";}
else if (dayName == "Wednesday"){dayInt = "3";}
else if (dayName == "Thursday") {dayInt = "4";}
else if (dayName == "Friday") {dayInt = "5";}
else if (dayName == "Saturday") {dayInt = "6";}
else dayInt = "7";
if(!c.data.daySchedule){
availAppts = totalApptsAllowed - count;}
else{
availAppts = 10;
var lookupsysid = c.data.dayLevelSysId;
var windowD = date.split(" ");
var windowDate = windowD[0];
var a = 0
while(a<bookingSchedule.length){
var value = bookingSchedule[a];
var test = JSON.parse(value);
if((windowDate < test.end_date && windowDate > test.start_date)){
if(test.bookable_days.indexOf(dayInt) >= 0 && (test.daily_start_time <= startTime && test.daily_end_time >= startTime))
availAppts = test.appointments_per_bookable_slot - count;
}
a++;
}
}
return availAppts;
}
and it should now take the day of the week into consideration based on the config in the instance.
Enjoy!!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-24-2025 11:15 PM
Hello,
I am facing a similar requirement.
I read the comments and edited the Appointment Booking widget as instructed, but the changes are not reflected.
var appointmentWindow = {
actualStart: '09:00',
actualEnd: '09:30',
available: true,
scheduled: false,
remainingSlots: 3
};
To verify the functionality, I added the above script to the server script and included the HTML as advised, but it is not working properly.
Here is the content of the HTML:
<div class="content">
<div ng-class="{'scheduledSlotContainer': c.checkIfMobileDevice() &&appointmentWindow.scheduled == true}" ng-if="c.slotsByDays[c.activeDate][key]" ng-repeat="appointmentWindow in c.getSlicedSlots(key)">
<button ng-class="{'appointmentSlot': appointmentWindow.actualStart != c.selectedWindow.actualStart, 'scheduledSlot': appointmentWindow.scheduled == true }" class="disabledOverlay" ng-attr-uib-tooltip="{{appointmentWindow.scheduled == true && !c.checkIfMobileDevice() ? c.scheduledSlotTextMessage : null}}" ng-show="appointmentWindow.scheduled == true" tabIndex=0>
<span ng-class="{'slotContent': appointmentWindow.actualStart != c.selectedWindow.actualStart }">{{appointmentWindow.start}}</span>
</button>
<button aria-label="{{c.appointmentWIndowAriaLabelStartText}}: {{appointmentWindow.reformatedSelectedDate}} - {{c.appWindowBtnTextStartTime}}:{{appointmentWindow.start}} - {{c.appWindowBtnTextEndTime}}:{{appointmentWindow.end}}" class="btn appointmentSlot"
ng-disabled="!appointmentWindow.available || appointmentWindow.scheduled == true" ng-class="{'appointmentSlot appointmentSlotSelected': appointmentWindow.actualStart === c.selectedWindow.actualStart, 'appointmentSlot': appointmentWindow.actualStart != c.selectedWindow.actualStart, 'disabledSlot': !appointmentWindow.available && appointmentWindow.scheduled != true, 'scheduledSlot': appointmentWindow.scheduled == true }" ng-click = "c.selectActiveSlot(appointmentWindow, $index)" aria-pressed="{{appointmentWindow.actualStart === c.selectedWindow.actualStart? true:false }}" tabIndex="0">
<span ng-class="{'slotContent slotContentSelected': appointmentWindow.actualStart === c.selectedWindow.actualStart, 'slotContent': appointmentWindow.actualStart != c.selectedWindow.actualStart }">{{appointmentWindow.start}}({{appointmentWindow.slots}}) </span>
</button>
<span ng-show="c.checkIfMobileDevice() && appointmentWindow.scheduled == true" class="scheduledSlotMsg">{{c.scheduleSlotMsg}}</span>
</div>
I would greatly appreciate your advice.
If there is any specific information you need, please let me know.
I will provide it.