Tasks Mind

We are now going to build an application that represents Tasks as Mindmap. In order to filter tasks we accept the project name from the URL. This enables us to trigger the app from a specific Project from CRM.

Create application in VTAP (tasks_mind). Navigate Main Menu > Platform > App Creator > Add App

Fill the Form And Save

Clicking on TasksMind App will open an editor where you can customise your App.

views/index.html

To build the Tasks MindMap we shall use jsMind library which provides various options to control the visualization of data (refer).

<!DOCTYPE html>
<html>
   <head>
       <title>Tasks Map</title>
       <meta charset="UTF-8">
       <meta name="viewport" content="width=device-width, initial-scale=1.0">
       <link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jsmind@0.5/style/jsmind.css"/>
       <link rel="stylesheet" href="resources/index.css">
   </head>
   <body>
       <div id="app">
           <div id="progress"></div>
           <div id="jsmind_container"></div>
       </div>
      
       <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
       <script src="https://cdn.jsdelivr.net/npm/jsmind@0.5/es6/jsmind.js"></script>
       <script src="https://cdn.jsdelivr.net/npm/jsmind@0.5/es6/jsmind.draggable-node.js"></script>
       <script src="resources/vcap.js"> </script>
       <script src="resources/index.js"> </script>
   </body>
</html>

Click Ctrl+s to Save the changes

resources/index.js

window.addEventListener('DOMContentLoaded', function() {

      // Collect parameters that comes in through URL
	var req_params = {};
	location.hash.substr(1).split("&").forEach(
		function(item) {
                req_params[item.split("=")[0]] = 
                   decodeURIComponent(item.split("=")[1])
            }
      );

	if (!req_params["name"]) {
		// No project selected.
		return;
	}

	var all_tasks_filterid = 19; /* "All Tasks" Filter ID   */
      var task_skip_closed = true; /* Turn off closed records */
	
      var project_name = req_params["name"];
	
	// Additional fields that are required if Filter is missing it.
      var extrafields = ["parent_task", "dependent_on"];

	// Filter custom condition
	var q = [];
	q.push(["related_project", "equal", project_name]);
	
	if (task_skip_closed) {
          q.push(["isclosed", "equal", 0]);
      }
      var record_options = {
          module: "Tasks", 
          filterid: all_tasks_filterid, 
          q: [q], 
          extrafields: extrafields
      };

	// Helper functions
	function determine_task_color(task) {
		return task.isclosed == 1? "#5cb85c" : "#428bca";
	}

      var mind = {
		"meta": {
			"name": project_name
		},
		"format": "node_array",
		data: [
			{id: "project", isroot:true, topic: project_name }
		]
	};

	jQuery("#progress").html("Loading...");

	VCAP.userapi.records.get(record_options, (e, tasks) => {
		jQuery("#progress").hide();

		var taskids = {};

		// Main tasks
		for (var i = 0, len = tasks.length; i < len; i++) {
                  /* avoid duplicate parsing */
			if (taskids[tasks[i].id]) continue;
 
			if (tasks[i].parent_task && tasks[i].parent_task.id > 0) 
                       continue;

			mind.data.push({ 
                     id: tasks[i]["id"], 
                     parentid: "project", 
                     topic: tasks[i]["label"], 
                     "background-color":determine_task_color(tasks[i]) 
                  });

			taskids[tasks[i]["id"]] = true;
		}


		// Sub tasks
		for (var i = 0, len = tasks.length; i < len; i++) {
                  /* avoid duplicate parsing */
			if (taskids[tasks[i].id]) continue; 

			if (tasks[i].parent_task && tasks[i].parent_task.id > 0){
				mind.data.push({ 
                          id: tasks[i]["id"], 
                          parentid: tasks[i].parent_task["id"], 
                          topic: tasks[i]["label"], 
                          "background-color":determine_task_color(tasks[i])
                        });
			}
			taskids[tasks[i]["id"]] = true;
		}
		
		var options = {
			container: 'jsmind_container', 
			theme: 'primary',
			editable: false
		};
		
		var jm = new jsMind(options);
		jm.show(mind);
	
            // Navigate to task on click of node
		jm.add_event_listener((type, data) => {
                if (data.evt == "select_node" && data.node != "project") {
                    var taskUrl = "/view/detai
l”;
                    taskUrl += "?module=Tasks&id="+ data.node;
			  
                    window.open(taskUrl);
                }
		});		
	});
});

Click Ctrl+s to Save the changes

resources/index.css

#jsmind_container {
	height: 100vh;
	width: 100%;
}

Click Ctrl+s to Save the changes

Publish the changes.

The application will be available at (/myapps/tasks_mind) but won't be of much use if the parameter (name) is not sent through the URL. We shall achieve this linking in the next step.

Linking app from CRM

Navigate VTAP > Module Designer > TAP Script provides us capability to build In-app custom components for the CRM. Using this we can add custom action on every Project Row of the List View.

Choose Projects and Create TAP Script - which can work across module.

NOTE: Target Module can be selected as Project to make it available only for Projects ListView. For demonstration we will achieve this through code.

var Project_Component_TasksMap = VTAP.Component.Core.extend({
   created: function() {
       var viewModule = App.module();
       if (viewModule == "Project") {
           VTAP.Component.Register( 'LIST_ROW_BASIC_ACTION', {}, 
               VTAP.Component.Load('TasksMapAction', 'Project'));
       }
   }
});

var Project_Component_TasksMapAction = VTAP.Component.Core.extend({
   props: ['record'],
   methods: {
       navigateToTasksMap: function() {
           var url = "/myapps/tasksmind";
           if (this.record._moduleName == "Project") {
               url += "#name=" + this.record.label;
           }
           window.open(url);
       }
   },
   template: `
       <li title="Tasks Map" class="list-inline-item c-pointer px-2 m-0" 
           @click="navigateToTasksMap()"><i class='fas fa-tasks'></i>
       </li>`
});

Save and Publish to make the component functional.

Open Projects ListView and hover on the record to see the new button added.

NOTE: If you don’t see it - try clearing your browser cache or use a private window.

Now clicking on the button would open /myapps/tasks_mind#name=project_name parameter which makes its work.