Practice Tutorial: Creating a new Kaizala Action
Overview
In this tutorial, we will create a custom Kaizala Action utilizing the extensible Action framework provided by the Kaizala platform.
We will create an “Ask Feedback” Action which will allow Kaizala users to ask for feedback from other users in terms of 1-to-5 star ratings on questions asked – along with any comments they would like to provide.
You can download sample Kaizala Action packages from here.
Pre-requisites
- A smartphone (Android/iOS) with Kaizala app installed
- An Office365 Account
- Access to an HTML/JS editor (notepad/Visual Studio Code/etc.)
Steps to create a sample Action
Step 1: Create a new directory for your Action package
- Create a new directory on your desktop & name it “SampleAction”
- Place all the subsequent files in this directory
Step 2: Define a data model for your Action
First, we need to define the data model which will be used in our custom Action
The Kaizala Actions are currently form based data models. So, we will first need to define the ‘questions’ we need to include in our form.
Since this is a “Ask Feedback” action – we will plan to use two pieces of data
- Numeric rating (value 1 to 5)
- Comment/Verbatim feedback if any
Kaizala Forms infrastructure supports below question types:
Question Type Description SingleSelect Single choice question with options MultiSelect Multiple choice question with options Text Question with a plain text answer Numeric Question with a numeric answer Location Question with a location answer DateTime Question with DateTime answer Image Question with a picture as an answer For our two questions we will use Question Type of 'Numeric' & 'Text' for Rating number & comments respectively
- Create a new file called “appModel.json” and place the following content in the file. The file contains the two questions listed in order
{ "questions": [{ "title": "Rating", "type": "Numeric" }, { "title": "Comments", "type": "Text" } ], "title": "Ask Feedback", "visibility": "All", "isAnonymous": false, "isResponseAppended": false }
- Note: Above code also contains additional metadata we will learn about later (out of scope for this session)
- Save the file
Step 3: Define a view for creating the request for feedback
- We will now create a new HTML file which will be used to create new instances of our Kaizala Action
- This is the view that is invoked when the icon is tapped from the palette. In this view, we would like to ask them what they would like feedback on.
- Create a new file called “CreationView.html” and place the below HTML in the file
<html> <head> </head> <body onload="onCreatePageLoad()"> <br/> <br/> <div> <p>What would you like feedback on?</p> <br/> <br/> <input type="text" name="description" id="description" placeholder="Enter question text here" value=""> </div> <br/> <br/> <div> <input type="submit" name="submit" value="Submit" onclick="submitData()"> </div> </body> </html>
- Save the file
Step 4: Include the Kaizala Forms JS SDK
- To make it easy for developers to leverage the Forms Infrastructure, Kaizala provides a JavaScript wrapper library that you can include in your custom Actions
- Download the file below & place it in the same directory as your datamodel.json and CreationView.html
Step 5: Use the Kaizala Forms JS SDK to submit form
Add the following code snippet in the
<head>
element of your “CreationView.html” to refer to the Kaizala Forms JS SDK<head> <script type="text/javascript" src="KASClient.js"></script> </head>
Now, when the user clicks submit, we need to verify that he has entered a question to ask feedback on – and create the Form Instance
We also need to instantiate the form on page load. Add the following JavaScript snippet to your “CreationView.html” inside the
<head>
element<script type="text/javascript"> var _form; // type: KASForm // Below will be called on onload of CreationView.html function onCreatePageLoad() { KASClient.Form.initFormAsync (function (form, error) { if (error != null) { showAlert("Error:initFormAsync:" + error); return; } _form = form; }); } function submitData() { var description = document.getElementById("description").value; if (description == null || description == "") { KASClient.App.showNativeErrorMessage("Please enter the details for what you would like feedback on."); } else { // Set the description to form title _form.title = description; // Finally send the request KASClient.Form.submitFormRequest(_form); } } </script>
Save the file
Step 6: Create the Action package manifest
Now that we have a semblance of what we want to achieve and successfully created a view – we will now create the package manifest file that refers to these files.
Package manifest file provides essential information for the Kaizala platform for it to recognize and run your custom Kaizala action.
We will create a package manifest and specify the following things:
- Display name for your Kaizala Action
- Custom Id for the Action
- Mapping of the creation view to our CreationView.html page
Before that we will need an icon for the Action package. Download the icon file & save it as icon.png in the same folder as the other files.
Create a new file called “package.json” and add following snippet to the file. Ensure that you edit the id to make your Action unique/distinct
{ "id": "com.microsoft.mobile.kaizala.swads.FeedbackSample", "schemaVersion": 1, "version": 1, "minorVersion": 1, "providerName": "Microsoft Inc.", "displayName": "Ask Feedback", "description": "Custom action to collect feedback.", "icon": "icon.png", "appModel": "appModel.json", "views": { "CreationView": { "labelHeader": "Feedback requested", "sourceLocation": "CreationView.html" } } }
You can configure the card that appears on Kaizala chat canvas by specifying the strings in the package manifest.
Modify the “Views” object in you package manifest to match the below snippet:
"views": { "CreationView": { "labelHeader": "Feedback requested", "sourceLocation": "CreationView.html" }, "ChatCanvasCardView": { "labelResponded": "You have provided feedback.", "labelRespondToForm": "PROVIDE FEEDBACK", "isResponseEditable": true } }
Save the file
Step 7: Create the response view
- Now we will create the view that appears to Kaizala users responding to an instance of our action
- Create a new file call ResponseView.html and insert the below snippet in the file
- We will do the following
- Add radio button selection for the rating
- Prepare a form response object and code to submit the form
<html> <head> <title></title> <script type="text/javascript" src="KASClient.js"></script> <script> var _form; // type: KASForm var _myFormResponses; // type: KASFormResponse[] // Below will be called on onload of ResponseView.html function onResponsePageLoad() { KASClient.Form.getFormAsync(function (form, error) { if (error != null) { KASClient.App.showNativeErrorMessage("Error:getFormAsync:" + error); return; } _form = form; // Document title would be the form title document.getElementById("title").innerHTML = _form.title; KASClient.Form.getMyFormResponsesAsync(function (responses, error) { if (error != null) { KASClient.App.showNativeErrorMessage("Error:getMyFormResponsesAsync:" + error); return; } _myFormResponses = responses; // Render previous response, if any if (isCurrentUserResponded()) { var rating = _myFormResponses[0].questionToAnswerMap["0"]; var remark = _myFormResponses[0].questionToAnswerMap["1"]; var options = document.getElementsByName('option'); options[rating - 1].checked = true; document.getElementById("description").value = remark; document.getElementById("description").placeholder = ""; } }); }); } function submitData() { var selectedOption = getSelectedOption(); var remark = document.getElementById("description").value; submitFormResponse(selectedOption, remark); } function getSelectedOption() { // Check which radio button is checked var options = document.getElementsByName('option'); for (var i = 0; i < options.length; i++) { if (options[i].checked) { return parseInt(options[i].value); } } } // Below will be called when responder submits a new response function submitFormResponse(selectedOption, remark) { if (remark == null || remark == "") { KASClient.App.showNativeErrorMessage("Please fill remark"); } else if (selectedOption == "") { KASClient.App.showNativeErrorMessage("Please select one option"); } else { // For submitting response a question-answer // map is required, lets create that! var questionToAnswerMap = JSON.parse("{}"); questionToAnswerMap[0] = selectedOption; questionToAnswerMap[1] = remark; var responseId = null; var isEditingPreviousResponse = false; // If there is a previous response, update it if (isCurrentUserResponded()) { responseId = _myFormResponses[0].id; isEditingPreviousResponse = true; } // Finally submit the response KASClient.Form.sumbitFormResponse(questionToAnswerMap, responseId, isEditingPreviousResponse); } } function isCurrentUserResponded() { return _myFormResponses && _myFormResponses.length > 0; } </script> </head> <body onload="onResponsePageLoad()"> <br/> <br/> <div> <p id="title"></p> </div> <div> <br/> <br/> <div> <p>Select your rating:</p> <br/> <br/> <div> <label>1</label> <input type="radio" name="option" id="option0" value="1" checked> </div> <div> <label>2</label> <input type="radio" name="option" id="option1" value="2"> </div> <div> <label>3</label> <input type="radio" name="option" id="option2" value="3"> </div> <div> <label>4</label> <input type="radio" name="option" id="option3" value="4"> </div> <div> <label>5</label> <input type="radio" name="option" id="option4" value="5"> </div> </div> <br/> <br/> <div> <p>Comments</p> <input type="text" name="description" id="description" placeholder="Please enter your comments here."> </div> <br/> <br/> </div> <div class="footer"> <input type="submit" name="submit" value="Submit" onclick="submitData()"> </div> </body> </html> `````
Step 8: Create the summary view
Now we will create the view that appears to Kaizala users viewing the summary of the action
Create a new file call SummaryView.html and insert the below snippet in the file. This will create a summary view of the total ratings and comments
<html> <head> <title></title> <script type="text/javascript" src="KASClient.js"></script> <script type="text/javascript"> // Globals var _form; // type: KASForm var _myFormResponses; // type: KASFormResponse[] var _formSummary; // type: KASFormFlatSummary var _users; // type: Dictionary<UserId: KASUser> // Below will be called on onload of SummaryView.html function onSummaryPageLoad() { KASClient.Form.getFormAsync(function (form, error) { if (error != null) { KASClient.App.showNativeErrorMessage("Error:getFormAsync:" + error); return; } _form = form; KASClient.App.showProgressBar("Fetching summary"); KASClient.Form.getFormSummaryAsync( function (flatSummary, processedSummary, error) { // In this callback data is fetched from local database if (error != null) { KASClient.App.showNativeErrorMessage("Error:getFormSummaryAsync:callback1:" + error); return; } onGetSummary(flatSummary); }, function (flatSummary, processedSummary, error) { // In this callback data is fetched from server KASClient.App.hideProgressBar(); if (error != null) { KASClient.App.showNativeErrorMessage("Error:getFormSummaryAsync:callback2:" + error); return; } onGetSummary(flatSummary); } ); }); } function onGetSummary(summary) { _formSummary = summary; KASClient.App.getUsersDetailsAsync(_formSummary.getRespondedUserIds(), function (users, error) { if (error != null) { KASClient.App.showNativeErrorMessage("Error:getUsersDetailsAsync:" + error); return; } _users = users; // Document title would be the form title document.getElementById("title").innerHTML = _form.title; // Get all responses of the user, and find the average var totalRating = 0; var responseCount = 0; for (var userId in _users) { var userResponses = _formSummary.getResponsesForUserId(userId); // type: Dictionary<QuestionId, string[]> totalRating += parseInt(userResponses["0"][0]); responseCount += 1; } document.getElementById("avg").innerHTML = totalRating / responseCount; }); } </script> </head> <body onload="onSummaryPageLoad()"> <div class="header"> <p id="title">Title</p> <br/> <br/> <p id="rating">Average Rating: </p> <p id="avg">avg</p> </div> </body> </html>
Edit the Views object in your package manifest to the following:
"views": { "CreationView": { "labelHeader": "Feedback requested", "sourceLocation": "CreationView.html" }, "ChatCanvasCardView": { "labelResponded": "You have provided feedback.", "labelRespondToForm": "PROVIDE FEEDBACK", "isResponseEditable": true }, "ResponseView": { "labelHeader": "Provide feedback", "sourceLocation": "ResponseView.html" }, "ResponseResultsView": { "labelPageHeader": "Feedback summary", "sourceLocation": "SummaryView.html" } } `````
Step 9: Create the Kaizala Action package
- Zip all the files into a single zip file
- Make sure that the zip does not include another directory inside it – but the files are present at the root directory of the zip
Step 10: Log in to the Kaizala Management Portal
- Open a browser window and navigate to https://manage.kaiza.la/
- On the top left corner, click on “Sign In”
- Enter your Office365 credentials to log in to the portal. If requested, grant permissions to the management portal to access your profile information (needed only for the first time)
- Tap on “Add Phone number” on the top right hand corner & verify your phone number
Step 11: Upload the action package
- Navigate to “Actions” using the left Navbar
- From the drop-down on the right – choose “Import Action” and click Start
- Click on upload – and select the zip file you created
- Click on import
- Once the package is successfully imported, confirm by browsing to Actions again.You should see your Action listed under “created by this Id”
- You can test this Action by following steps here
Step 12: Create a new Kaizala Group
- On Kaizala Management portal, navigate to “Groups” using the left navbar
- Tap on “Create Group”
- Name the group “Kaizala Actions Testing”
- Verify that the group appears on your Kaizala app
Step 13: Publish the Action to the Group
- Navigate to “Groups” using the left Navbar
- Tap on the group name you created in Step 13
- Tap on the “Actions” tab
- Select the Action you created in Step 12 & click “Add Action”. You should now see your custom Kaizala Action appearing on the Discover tab
Step 14: Use custom Kaizala Action
- Add the custom Action to your palette and use it in your group.
Feedback
https://aka.ms/ContentUserFeedback.
Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see:Submit and view feedback for