Using modal to verify input from user when saving

6 댓글

  • Greg Katechis
    Zendesk Developer Advocacy

    Hi Daniel! Our team actually built an example app that is close to this situation that you can look at in its entirety here. The specific stuff that you'll probably find useful is going to start on line 90, which highlights how we pass data from the modal to the sidebar. In your situation, you'll want to start with an async function on save that remains open until the user presses ok or cancel. For the save and cancel actions you'll just use the reference docs in the link that you provided and you'll be good to go!

    0
  • Daniel Wennerdahl

    Hi, Greg-Katechis

    Thank you for the example. However I cant get this to work since I'm within the client.on('ticket.save'). The app has functionality that cant be loaded in the modal window so can't use the same JS file there. 

    Even if the listeners could be registered, there needs to be a promise to "pause" the save process, that in turn needs to be resolved to proceed.

    I cant find anything in the documentation that covers this more then the events them self's. Maybe I'm just bad at searching :D 

    client.on("ticket.save", async () => {      
      /**
       * 1. pause the save process through a promise
       * 2. wait for user ok/cancel
       * 3. on ok => save
       * 4. on cancel => abort save
      */
    });
    0
  • Corentin BERTEAU

    Hi Daniel Wennerdahl

    I'm trying to do the same thing, but from the moment my function is asynchronous, client.on('ticket.save', async function () {... return false;} doesn't work.

    The only way I've found, and it's very ugly, is to call a nonexistent variable to block the sending by creating an error... If I find a solution or if I manage to make it work, I'll keep you informed.

    It's possible anyway; the 'cancel submit ticket' application does exactly what we're trying to reproduce.

    ----
    * update *
    My bad, to cancel async function : return Promise.reject(false);

    I will review my code and share my approach here asap. 

    0
  • Daniel Wennerdahl

    Hi Corentin BERTEAU

    I found a solution for this, it not pretty, but it works =)

    I have an async method in the client.on event that runs a while loop, and every sec checks for status variabels set from the modal through custom triggers.

    This post show how these custom triggers can be created: 

     
    Example that incorporates everything: (in the app. main.js)
    // --- Modal flags
    var modalCancel = false;
    var modalSave = false;


    client.on("ticket.save", async () => {      

        // Open modal window
        var modalResult = await openModal();

        // If the agent has clicked Save & Close return true so that the ticket can save
        // Or if ticket is to be saved due to errors in the app
        if(modalResult === true){
          resetModal(); // Resets the modalFlags
            return Promise.resolve(true);
        } 
    ...

    });


    async function openModal(){
        var modalContext = await client.invoke('instances.create', {
            location: 'modal',
            url: 'assets/modal.html',
            size: { 
                width: '450px',
                height: '230px'
            }
        });

        var modalClient = await client.instance(modalContext['instances.create'][0].instanceGuid);
     
        client.on('modalSaveAndClose', () => {
            console.log("Event: Save and Close");
            modalSave = true;
        });

        // if the user closes the modal by clicking the X or clicks outside the window
      // Note that this listens to the modal instance and not the base client.
        modalClient.on('modal.close', () => {
            console.log("Event: modal.close Fired");
            modalCancel = true;
        });
        
        // This is a timer that waits for user action. Cancel or save
        // It checks every second if anything has changed.
        var open = true;
        while(open){
          if(modalCancel == true && modalSave == false){
                console.log("Modal Canceled");
                modalCancel = false; 
                return false;
            }

          if(modalSave == true){
                console.log("Modal OK / Save and Close");
                modalSave = false;
                return true;
            }

            await new Promise(r => setTimeout(r, 1000));
        }
    }

    In the modal window (modal.js)
    (The function saveAndClose() is used on the Save and close button's click event)
    ...

    // --- Get the parent instance ID ------------------------------------
    var ticketClientPromise = client.get('instances').then(function(instancesData) {
        var instances = instancesData.instances;
        for (var instanceGuid in instances) {
            if (instances[instanceGuid].location === 'ticket_sidebar') {
                return client.instance(instanceGuid);
            }
        }
    });

    // --- Create custom trigger for the save and close process. This is then listened for in the app
    function saveAndClose(){
        // Create OK trigger for app to save ticket
        ticketClientPromise.then(function(ticketClient){
            console.log("Event: modalSaveAndClose created");
            ticketClient.trigger("modalSaveAndClose");
        });

        client.invoke('destroy');
    }
     
    0
  • Corentin BERTEAU

    Hi,

    Thanks for sharing this, my way to do this below but can't find a solution to update modal.html with some ticket info.

    let client = ZAFClient.init();
    let modalClient = null;
    let parentClient = null;
    let data;

    function displayModal() {
      return new Promise((resolve) => {
        client.invoke('instances.create', {
          location: 'modal',
          url: 'assets/modal.html',
          size: {
            width: '450px',
            height: '200px'
          }
        }).then(function(modalContext) {
          // The modal is on screen now
            var modalClient = client.instance(modalContext['instances.create'][0].instanceGuid);
            // The modal has been closed
            modalClient.on('modal.close', function() {
              resolve('cancel');
            });
          client.on('modal.response', (response) => {
              resolve(response);
            });
        });    
      });
    }


    // Send the selected tags from the modal to the ticket sidebar
    async function confirmChange() {
      console.debug("confirmChange called...");

      const context = await client.context();
      let instanceGuid = context.instanceGuid;
      modalClient = client.instance(instanceGuid);

      parentClient.trigger('modal.response', 'confirm');

      modalClient.invoke('destroy');
    }

    // Close the modal without sending changes to the ticket sidebar
    async function cancelChange() {
      const context = await client.context();
      let instanceGuid = context.instanceGuid;
      modalClient = client.instance(instanceGuid);
      parentClient.trigger('modal.response', 'cancel');

      console.debug("cancelChange called...");
      modalClient.invoke('destroy');

    }


    // Ticket sidebar and modal button click listeners
    window.addEventListener('DOMContentLoaded', function (event) {

      let displayModalButton = document.querySelector("#displayModal");
      let confirmButton = document.querySelector("#confirm");
    let last_comment = document.querySelector("#last_comment");
      let cancelButton = document.querySelector("#cancel");
      let group_is_helpdesk = false;

      client.get('currentUser').then(function (currentUserData) {
        if (currentUserData && currentUserData.currentUser) {
          const currentUser = currentUserData.currentUser;
          const userGroups = currentUser.groups;
          userGroups.forEach(function (group) {
            if(group.id === 8995665084317)
            {
              let test = group.name;
              group_is_helpdesk = true;
            }
          });
        } else {
          console.error('Les données de l\'utilisateur actuel ne sont pas définies.');
        }
      }).catch(function (error) {
        console.error('Erreur lors de la récupération des données de l\'utilisateur actuel:', error);
      });

      client.on('ticket.save', async function () {
        if(group_is_helpdesk)
        {
          console.log(group_is_helpdesk);
          try {
            data = await client.get('ticket');  
            if (data && data.ticket) {
              console.log(data);
              const currentStatus = data.ticket.customStatus.name;
              if (currentStatus === 'Transfert vers SAV') {
                const userConfirmed = await displayModal();
                console.log(userConfirmed);
                if (userConfirmed === 'cancel') {
                  console.log('L\'utilisateur a annulé l\'opération.');
                  throw new Error('Annulation de la sauvegarde');
                }
                console.log('Le ticket peut être sauvegardé.');
                return "Transfert SAV en cours..."
              } else {
                throw new Error('Le statut n\'est pas "Transfert vers SAV".');
              }
            } else {
              throw new Error('Les données du ticket ne sont pas définies.');
            }
          } catch (error) {
            console.error(error.message);
            return Promise.reject(false);
          }
        } else {
          return Promise.reject(false);
        }
      });

      if (last_comment) {
        console.log('ici');
        console.log(data);
        last_comment.innerText = test;
      }

      if (confirmButton) {
        confirmButton.addEventListener("click", function () {
          console.log("Add Tags Button clicked.");
          confirmChange();
        });
      }

      if (cancelButton) {
        cancelButton.addEventListener("click", function () {
          console.log("Cancel Button clicked.");
          cancelChange();
        });
      }

    });

    // Framework events 

    client.on('app.registered', function () {

      // Listen for the modal registration
      client.on('instance.registered', function (context) {
        if (context.location === 'modal') {
          let instanceGuid = context.instanceGuid;
          modalClient = client.instance(instanceGuid);

          // Trigger the event to send the parent instance GUID to the modal
          modalClient.trigger(
            'send_parent_client_guid_event',
            client._instanceGuid
          );
        }
      });

      // Receive the parent client instance 
      client.on('send_parent_client_guid_event', function (parentClientGuid) {
        console.debug('Receiving parent guid...', parentClientGuid);
        parentClient = client.instance(parentClientGuid);
      });
    });
    0
  • Daniel Wennerdahl

    Hi Corentin BERTEAU

    You can send parameter along with the invoke of the modal, then read them in the modal/iframe.

    var modalContext = await client.invoke('instances.create', {
          location: 'modal',
          url: 'assets/modal.html?jsonObject=' + this.jsonObject,
          size: {
              width: '450px',
              height: '230px'
          }
        });

    Important here is also that the jsonObject must be uri encoded.

    example:

    var jsonObject = encodeURIComponent(JSON.stringify(jsonData));
     
     
    In the modal
     
    function getDataFromUrl(){

      const searchParams = new URLSearchParams(window.location.search);

      if(searchParams && searchParams.has("jsonObject")){
          var t = searchParams.get("jsonObject");
          const jsonData = JSON.parse(t);

    // Do things with jsonData

      }
    }
     
    0

댓글을 남기려면 로그인하세요.

Zendesk 제공