Build your own invitation (pt 2)

In the Box Configurations

Many small alterations to your invitation are possible without leveraging API extension points. These configurations can be made through the normal release process for updating your ForeSee® Web SDK. Here are some examples of what you can revise with a standard configuration:

Desktop Experience

  • Invitation text, logos, fonts, and colors.

Mobile Experience

  • Dark and Light color theme options.
  • Customize invitation page 1 and text, button labels, fonts, and colors.

Full details about customization options for in the box configurations can be found at Personalize Your Survey Invitation.

Breaking Out of the Box

To move beyond these basic configuration options and ship a fully rebuilt user experience for your survey, you need different tools. Your ForeSee® SDK ships with an extension API for customizing your ForeSee invitation. If your organization has migrated to 19X era ForeSee code, you can start today.

Limitations and Trade-offs

Shipping a custom invitation with these extension endpoints completely replaces the native invitations included with your ForeSee® SDK.

You must account for all possible behaviors in your redesigned invitation. This includes all device groups where the code is allowed to execute. So, if you replace an invitation for one screen type, you must either provide a custom implementation suitable for all three screen groups (desktop, mobile, tablet), or, limit your custom invitation code to be invoked only on one specific device group using a tag manager or another solution.

Extension APIs 101

ForeSee's extension APIs are all designed to work as callbacks registered to the lifecycle of your ForeSee® SDK. Before invoking API functions, you must ensure that the ForeSee® SDK has loaded and made API functions available. Since the ForeSee SDK loads asynchronously, we use a ready event to delay execution of code.

fsReady(() => {
    // Do some custom work after ForeSee is ready. 
});

Introducing the Invitation Handler

Your custom invitation is also registered using a callback pattern. We begin by registering a custom invitation; invoking the invitation API's subscribe method.

fsReady(() => {
    FSR.customInvitationRequested.subscribe(
        function(inviteType, acceptHandler, declineHandler, abandonHandler) {
            // Display your HTML invitation here
        }
    );
});

Subscribe expects to receive a function definition, which will, in turn, accept four attributes.

Custom Invitation Attributes

Our subscribe function supplies four attributes:

  1. inviteType (String) - denotes the invitation type. (TRACKER, INSESSION, EMAIL, SMS, SMSEMAIL)
  2. acceptHandler (Function) - Invoked to accept the survey invitation.
  3. declineHandler (Function) - Invoked to decline the survey invitation.
  4. abandonHandler (Function) - Invoked to abandon the survey invitation. In some invitation types (SMS, EMAIL, and SMS Email), an abandonment causes the invitation to display again on the next non-suppressed page.

The body of your custom invitation function is used to modify the Document Object Model (DOM) and add an invitation layer when the ForeSee invitation is triggered.

Getting Started

To begin work, add a dimmer layer to the entire document to serve as a backdrop to the invitation. Clicking this area invokes the abandonHandler function.

In the following examples, an external CSS stylesheet is referenced in order to keep the code as clean as possible. jQuery is also being used to simplify interactions and the mutation of the DOM.

Creating an Invitation Layer

Before wiring functionality, you must create an invitation layer, hidden in the DOM, awaiting a request for a survey invitation.

$(document).ready(()=>{
  if(typeof fsReady === "function"){
      fsReady(()=>{
        // Pre-mount invitation to page to allow pre-loading of images.
        // visibility defaults to hidden by invite.css
        $("body")[0].append($.parseHTML(inviteTemplate)[0]);
        requestCustomInvitation();
    });
  }
});


const requestCustomInvitation = () => {
   FSR.customInvitationRequested.subscribe(function(inviteType, acceptHandler, declineHandler, abandonHandler) {
      // Build your HTML invitation in the DOM here
      console.log("Invitation requested");
      // Reveal the ForeSee invitation by toggling visibility
      let invite = $("#foreseeInvite");
      invite.css("visibility","visible");
   });
};

const inviteTemplate = 
`<div id="foreseeInvite">
  <div id="fadeLayer">
    <div id="positionLayer">
      <div id="heroLayer">
        <h1 id="headingLayer">Your Feedback Helps Us Serve You Better</h1>
      </div>
      
      <div id="inviteBody">
        <div id="textLayer">
          <p>
            Thank you for visiting our website. You have been selected to participate in a brief customer
            satisfaction survey to let us know how we can improve your experience.
          </p>
        </div>
        <div id="buttonLayer">
          <button id="acceptForeSee" class="button btn-success inviteButton">Yes, Take Survey!</button>
          <button id="declineForeSee" class="button btn-danger inviteButton">Not Right Now.</button>
        </div>
      </div>
    </div>
  </div>
</div>`;

Managing Basic User Actions

Now that the user interface is in place, you can connect the necessary invitation actions to manage user interactions.

$(document).ready(()=>{
  if(typeof fsReady === "function"){
    fsReady(()=>{
      //Execution delayed until FSR SDK is ready and APIs are available.
      console.log("FSR is Ready");
      // Pre-mount invitation to page to allow pre-loading of images.
      // visibility defaults to hidden by invite.css
      $("body")[0].append($.parseHTML(inviteTemplate)[0]);
      requestCustomInvitation();
    });
  }
});

const requestCustomInvitation = () => {
   FSR.customInvitationRequested.subscribe(function(inviteType, acceptHandler, declineHandler, abandonHandler) {
      // Build your HTML invitation in the DOM here
      console.log("Invitation requested");
      // Reveal ForeSee invitation by toggling visibility
      let invite = $("#foreseeInvite");
      invite.css("visibility","visible");
      
      //HandleBasic Events
      $("#foreseeInvite").on("click",(e) => handleAbandonClicks(e,abandonHandler));
      $("#acceptForeSee").on("click",(e) => handleAcceptClicks(e,acceptHandler));
      $("#declineForeSee").on("click",(e) => handleDeclineClicks(e,declineHandler));
   });
};

const handleAbandonClicks = (e,handler) => {
  let tid = e.target.id;
  if(["fadeLayer","foreseeInvite"].includes(tid)) runHandler(handler);
}

const handleAcceptClicks = (e,handler) => {
  runHandler(handler);
}

const handleDeclineClicks = (e,handler) => {
  runHandler(handler);
}

const runHandler = (handler) => {
  handler();
  $("#foreseeInvite").remove();
}

Changing Default Page Flows

You are not limited to replicating default ForeSee behaviors. Using the event handler functions provided by the API, you can change the control-flow of your invitation.

$(document).ready(()=>{
  if(typeof fsReady === "function"){
    fsReady(()=>{
      //Execution delayed until FSR SDK is ready and APIs are available.
      console.log("FSR is Ready");
      // Pre-mount invitation to page to allow pre-loading of images.
      // visibility defaults to hidden by invite.css
      $("body")[0].append($.parseHTML(inviteTemplate)[0]);
      requestCustomInvitation();
    });
  }
});

const requestCustomInvitation = () => {
   FSR.customInvitationRequested.subscribe(function(inviteType, acceptHandler, declineHandler, abandonHandler) {
      // Build your HTML invitation in the DOM here
      console.log("Invitation requested");
      // Reveal ForeSee invitation by toggling visibility
      let invite = $("#foreseeInvite");
      invite.css("visibility","visible");
      
      //HandleBasic Events
      $("#foreseeInvite").on("click",(e) => handleAbandonClicks(e,abandonHandler));
      $("#acceptForeSee").on("click",(e) => handleAcceptClicks(e,acceptHandler));
      $("#declineForeSee").on("click",(e) => handleDeclineClicks(e,declineHandler));
   });
};

const handleAbandonClicks = (e,handler) => {
  let tid = e.target.id;
  if(["fadeLayer","foreseeInvite"].includes(tid)) runHandler(handler);
};

const handleAcceptClicks = (e,handler) => {
  runHandler(handler);
};

const runHandler = (handler) => {
  handler();
  $("#foreseeInvite").remove();
};

const handleDeclineClicks = (e,handler,retryRemaining = true) => {
  if(retryRemaining){
    $("#textLayer").replaceWith($.parseHTML(inviteTextRetry));
    $("#declineForeSee").text("No, I Hate Fun.");
    $("#declineForeSee").unbind("click").on("click",(e) => handleDeclineClicks(e,handler,false));
  } else{
    runHandler(handler);
  }
};

const inviteTextRetry = 
`
<div id="textLayer">
  <p><strong>Oops!</strong> It looks like you accidentally <em>declined</em> our survey invitation.
  Clearly you're not thinking straight. Ready for that survey now?</p>
</div>
`;

Handling Alternate Invitation Modes

At this point, you have wired together a full desktop experience. However, your mobile and tablet users need additional support. You can now alter the control-flow again to accept a phone number/email to deliver a survey link.

const handleAbandonClicks = (e,handler) => {
  let tid = e.target.id;
  if(["fadeLayer","foreseeInvite"].includes(tid)) runHandler(handler);
};

const handleAcceptClicks = (e,handler,inviteType) => {
  if(inviteType === "SMSEMAIL"){
    $("#inviteBody").replaceWith($.parseHTML(inviteBodyMobile));
    $("#acceptForeSee").on("click",(e) => handleMobileCollection(e,handler));
  }else{
    runHandler(handler);
  }
};

const handleMobileCollection = (e,handler) => {
  runHandler(()=>handler($("#contactInput").val()));
};

const handleDeclineClicks = (e,handler,retryRemaining = true) => {
  if(retryRemaining){
    $("#textLayer").replaceWith($.parseHTML(inviteTextRetry));
    $("#declineForeSee").text("No, I Hate Fun.");
    $("#declineForeSee").unbind("click").on("click",(e) => handleDeclineClicks(e,handler,false));
  } else{
    runHandler(handler);
  }
};

const inviteBodyMobile = 
`
<div id="inviteBody">
  <div id="textLayer">
    <p>
      <strong>Thank you for participating in our survey!</strong> Please provide a cellphone number,
      or email address where we can deliver your link at the end of your visit.
    </p>
  </div>
  <div id="buttonLayer">
    <input type="text" id="contactInput" />
    <button id="acceptForeSee" class="btn btn-success inviteButton">Submit</button>
  </div>
</div>
`;