Creating Your Own Views

Concierge Beacon allows you to replace our UI elements ("views") with your own. This allows you to customize what your customers see.

Introduction

This guide explains how to replace existing views in Concierge Beacon using your own views made with web components. Once you have replaced one or more of our web components, the Concierge Beacon system will automatically use your new web component instead of our pre-built component when displaying the view to your customer. This gives you complete control over how Concierge Beacon looks and functions for your customers.

This guide will focus on the most commonly replaced view: liveswitch-kiosk-home. There will be a list appended at the end with all the overridable views, although many are not recommended to override.

If you want to see a full example, you can simply scroll to the Full Examples section below.

Before You Begin

Your web component can be written using raw HTML or any framework (Angular, React, VueJS). In this guide, we will focus on plain HTML. In the appendix, I have attached additional resource links for creating web components using Vue, Angular, and React, along with more information about HTML web components. This guide assumes you have a simple HTML page, index.html, where you are placing the beacon.

Warning: While we do our absolute best to ensure backwards compatibility, as the product matures, some of these properties, events, and views may change. Always contact us at [email protected] before developing your own replacements so we can alert you of any changes that may come out prior to release.

Creating Your First Override

To start, we need to understand which view we want to override. In this example, we will override the Kiosk home page (refer to the appendix for component names). The kiosk home page element is called liveswitch-kiosk-home.

Creating the HTML Element

The first step when we override a view is to create a new web component to substitute into our controller. Lets start by making a simple Kiosk HTML Element that we can use instead of the normal Beacon provided one. This is done in JavaScript like so:

class KioskHome extends HTMLElement {
  static observedAttributes = [];
  constructor() {
  	// Always call super first in constructor
    super();
  }
  connectedCallback() {
    const evt = new Event("transition", {}); // Event to be dispatched on button click.
    const button = document.createElement("button");
    button.addEventListener("click", () => {
      this.dispatchEvent(evt); // Dispatch the event when the button is pressed.
    });
    button.innerHTML = "Click Me";
    this.appendChild(button);
  }
  disconnectedCallback() {}
  adoptedCallback() {}
  attributeChangedCallback(name, oldValue, newValue) {}
}

Here we have created a new class that inherits from HTMLElement. This class is called KioskHome. class KioskHome extends HTMLElement creates a bare minimum autonomous custom element, perfect for our use case.

The connectedCallback is fired when the component is added to the page. This is a perfect time to create our new view. In this case, we have created a new button using document.createElement("button") and added it to our web component using this.appendChild(button);.

Finally, the liveswitch-kiosk-home view must dispatch an event called "transition" this.dispatchEvent(evt); when the kiosk is ready to transition from the home page into a call. This generally happens on the press of a "go" button. All the events for views are available in the Appendix.

Registering Your HTML Element

Now that we have created our KioskHome class, we need to register it in our customElements registry.

customElements.define("liveswitch-kiosk-home", KioskHome);

The string value "liveswitch-kiosk-home" matches the view we wish to override, and the KioskHome variable matches the class we created above.

Using The Passed In Properties (Optional)

Every overridden HTML Element will receive properties from the controller that can be used to render the view. These properties are listed for each view in the Appendix. Let's take a look at our existing example and use the configured logo uploaded to Concierge Admin.

class KioskHome extends HTMLElement {
  static observedAttributes = [];
  constructor(kiosk) {
    // Always call super first in constructor
    super();
  }
  connectedCallback() {
    // Using the Kiosk Logo Property
    const image = document.createElement("img");
    image.src = this.kiosk?.Organization?.logoUrl || "default-logo.png";
    this.appendChild(image);

    // Using the Kiosk Text Property
    const paragraph = document.createElement("p");
    paragraph.innerHTML =
      this.kiosk?.website?.BubbleConfig?.templateWelcome ||
      "Default Welcome Text";
    this.appendChild(paragraph);

    const evt = new Event("transition", {}); // Note, when the start button is pressed, you need to dispatch this event, we will take care of the rest.
    const button = document.createElement("button");
    button.addEventListener("click", () => {
      this.dispatchEvent(evt); // Dispatch the above event when the button is pressed.
    });
    button.innerHTML = "Click Me";
    this.appendChild(button);
  }
  disconnectedCallback() {}
  adoptedCallback() {}
  attributeChangedCallback(name, oldValue, newValue) {}
}

The liveswitch-kiosk-home HTML Element receives a property called kiosk that has some basic information about the kiosk you configured in your Concierge Admin dashboard. In this example, we have created both a paragraph and image element and assigned them values from the kiosk property. It is always a good idea to have a backup value in case you unset the value in the Concierge Dashboard later on.

Using A Shadow Dom (Optional)

If you are loading a Kiosk, Bell, or QR Code inside of your existing webpage and don't want the stylesheet from your website to change how the Beacon looks, you can use a Shadow DOM to prevent styles from conflicting.

Example using a shadow DOM (not required in most use cases):

class KioskHome extends HTMLElement {
  static observedAttributes = [];
  constructor() {
    // Always call super first in constructor
    super();
  }
  connectedCallback() {
    const shadow = this.attachShadow({ mode: "open" }); // Open the shadow dom for this element.
    shadow.adoptedStyleSheets = []; // Any stylesheets to apply to the shadow dom, these will not affect styles existing on the page outside of the shadow dom.
    const evt = new Event("transition", {}); // Note, when the start button is pressed, you need to dispatch this event, we will take care of the rest.
    const button = document.createElement("button");
    button.addEventListener("click", () => {
      this.dispatchEvent(evt); // Dispatch the above event when the button is pressed.
    });
    button.innerHTML = "Click Me";
    shadow.appendChild(button);
  }
  disconnectedCallback() {}
  adoptedCallback() {}
  attributeChangedCallback(name, oldValue, newValue) {}
}

The change here is in the connectedCallback, where instead of appending directly to the main HTML DOM element, attach a Shadow DOM to our element and then append our button into it.

Wrapping Up

That's all that is required to override a view in Concierge Beacon. You can now create the view to look any way you would like.

Full Examples

liveswitch-kiosk-home

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Custom Kiosk App</title>
    <style>
      html,
      body,
      div#app {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
        font-size: 24px;
        touch-action: pan-x pan-y; /* Disables pinch-zoom but allows scrolling */
        transition: none;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <script type="module">
      // Create a web component for the element you wish to override.
      class KioskHome extends HTMLElement {
        static observedAttributes = [];
        constructor(kiosk) {
          // Always call super first in constructor
          super();
        }
        connectedCallback() {
          // Using the Kiosk Logo Property
          const image = document.createElement("img");
          image.src = this.kiosk?.Organization?.logoUrl || "default-logo.png";
          this.appendChild(image);

          // Using the Kiosk Text Property
          const paragraph = document.createElement("p");
          paragraph.innerHTML =
            this.kiosk?.website?.BubbleConfig?.templateWelcome ||
            "Default Welcome Text";
          this.appendChild(paragraph);

          const evt = new Event("transition", {}); // Note, when the start button is pressed, you need to dispatch this event, we will take care of the rest.
          const button = document.createElement("button");
          button.addEventListener("click", () => {
            this.dispatchEvent(evt); // Dispatch the above event when the button is pressed.
          });
          button.innerHTML = "Click Me";
          this.appendChild(button);
        }
        disconnectedCallback() {}
        adoptedCallback() {}
        attributeChangedCallback(name, oldValue, newValue) {}
      }

      // Override the component in Beacon with your own.
      customElements.define("liveswitch-kiosk-home", KioskHome);

      const init = async (concierge) => {
        const div = document.getElementById("app"); // This is your wrapper div, where we will load the kiosk into.
        const kiosk = await concierge.loadKiosk(
          "265048c0-8d70-4644-834b-fb53e5de50d3" // Your Kiosk Id (for development on staging, leave as is).
        );
        if (kiosk == null) {
          // What do you do if the above id is no longer working, this generally means the kiosk was deleted.
          alert(
            "This kiosk link is requesting is no longer avaialble. Please contact your administrator to fix the problem."
          );
          window.location.reload();
          return;
        }
        // This starts the kiosk home page.
        concierge.startInternalConference(
          div, // The element that should contain the kiosk, generally a full page wrapper.
          kiosk.website.BubbleConfig, // These are the settings for the kiosk (loaded from the settings area in Concierge Admin), can be overridden if you want.
          kiosk, // This is the kiosk that was loaded, do not change.
          {}, // This is user information provided to the kiosk at the start. It is not generally used in Kiosk Mode but send us an email if you'd like to know how to set this. (firstname, lastname, email and phone allowed).
          "KIOSK" // This is the mode to start. Possible values are: KIOSK, QRCODE, BELL
        );
      };
      window.liveswitchFn = window.liveswitchFn || [];
      window.liveswitchFn.push(init);
    </script>
    <!-- After development you will need to update this url to production. -->
    <script
      type="module"
      src="https://beacon.concierge.staging.liveswitch.com/bubble.js"
    ></script>
  </body>
</html>

Appendix

All Overridable Views

Every view in Beacon is overridable, however, you must use caution when overriding a view with a lot of logic. It is not recommended to adjust the following views: liveswitch-in-call, liveswitch-permission-check, and liveswitch-form. By adjusting those views, you run the risk of the application not functioning correctly. If you wish to adjust those views, please contact [email protected] to get a detailed guide on how to update them safely.

  • liveswitch-kiosk-home: This is the home page of the kiosk.
    • Properties: kiosk
    • Events: transition
  • liveswitch-bell: When using the bell on your web page, this would allow you to create a different button for users to click to start the Beacon.
    • Properties: config
    • Events: start
  • liveswitch-popup: This is the speech bubble that appears overtop the bell icon on page load.
    • Properties: config
    • Events: start, closePopup
  • liveswitch-window: When using the Beacon in the Bell configuration, it is standard to place it inside of a floating div. The outside wrapper for this floating div is the liveswitch-window.
    • Properties: none
    • Events: none
  • liveswitch-window-decoration: When using the liveswitch-window, this is the top bar for the floating div.
    • Properties: none
    • Events: close, minimize, maximize
  • liveswitch-dialing-in: This is the screen your customers see while waiting for an agent to answer the call.
    • Properties: config, isWindowed, sourceType
    • Events: none
  • liveswitch-in-call: This is the in-call view when an agent and customer are connected together.
    • Properties: config, userDetails,conversation, isWindowed, sourceType
    • Events: close
  • liveswitch-end-call: This screen is visible after an agent has ended the call but the user has not closed the screen.
    • Properties: config, isWindowed, sourceType
    • Events: close
  • liveswitch-permission-check: The screen that checks for webcam and mic permission before starting a call.
    • Properties: sourceType
    • Events: transition, close
  • liveswitch-form: Collects user information if required details are checked, or if a call is missed.
    • Properties: use, config, form, formSubmission, model, sourceType, userDetails
    • Events: transition, formSubmit, close

Resources for more information