Configurable Data Table Widget Content Selector

“I like being a beginner. I like the moment where I look at everyone and say, ‘I have no idea how to do this, let’s figure it out.'”
— Jon Acuff, Do Over: Rescue Monday, Reinvent Your Work, and Never Get Stuck

The reason that I needed to hack the out-of-the-box Data Table from URL Definition widget was because I had this vision of creating a small companion widget that could share the same page and allow a user to select what data they wanted to be displayed in the data table. To achieve my vision, I needed to be able to pass three elements in via the URL: the table, the filter, and the columns to display. The stock widget already supported the table and the filter, but the columns are controlled by the view, which also works to some extent, but for every desired selection/arrangement of columns, you would have to define a new view. That seemed like a little more work than I wanted to get into, so I tweaked the stock widget just a tad to allow me to simply pass in the names of the columns that I wanted to display in the order that I wanted to display them.

The Concept

The basic idea for my new widget is that it would allow the user to view their personal data in a variety of contexts by selecting one or more configurable parameters, and based on those parameters, produce a URL that would be consumed by the Data Table from URL Definition widget, which would do the heavy lifting of displaying the information. This companion widget could sit above or to the side of the main data table and provide a means for the user to control what information is displayed in the main content area. My hope was that I could create something completely driven off of an external configuration file, and customization and personalization could be handled entirely by altering the configuration file without having to crack open the widget itself. I saw my widget providing the user selectable options in three distinct areas: perspective, state, and table.

Perspective

The first option would be perspective, and would be the highest order in the hierarchy of choices. Other available options would be based on the selected perspective. The perspective represents the vantage point of the user. For example, in my little demonstration configuration, I have provided two perspective options for viewing data related to Incidents and Requests: Requester and Fulfiller. This is just one example. If you were looking at Project data, you might want perspectives such as Executive Sponsor, Project Manager, and Project Team. If you were looking at Change data, you might want something like Change Coordinator, Change Manager, and Change Implementer. Or if you wanted to look at work queues, your perspectives might be something like My Work and My Group’s Work. The point is that the widget should be designed in such a way that you can come up with whatever perspectives your particular situation might require, and all you should have to do is provide the appropriate configuration.

Each perspective will have three properties: name, label, and roles. The name and label properties are pretty self-explanatory; the roles property provides a means to limit the exposure of certain perspectives to just those folks for whom that perspective would be appropriate. In my example, everyone has access to the requester perspective, so there are no roles associated with that one. The fulfiller perspective, however, is only appropriate for those individuals involved in request fulfillment, so that perspective is linked to the itil role. Individuals who have the itil role will see the option for the fulfiller perspective and those that do not will not. Here is my example perspective configuration:

perspective: [{
	name: 'requester',
	label: 'Requester',
	roles: ''
},{
	name: 'fulfiller',
	label: 'Fulfiller',
	roles: 'itil'
}]

State

The second option would be state, which provides a means to classify the records from the selected table. For my example, I have defined 4 unique states: Open, Closed, Recent, and All. As with the example perspectives, the example configuration that I have chosen for my demonstration configuration represents just one possibility. You might prefer states such as Submitted, Approved, Assigned, and Completed. The intent is to set things up in such a way that you can configure any state options that are appropriate for your use case. The whole point is to create a widget in such a way that you can configure it in accordance with your own tastes and desires, and do so without having to alter the actual widget itself. Here is my example state configuration:

state: [{
	name: 'open',
	label: 'Open'
},{
	name: 'closed',
	label: 'Closed'
},{
	name: 'recent',
	label: 'Recent'
},{
	name: 'all',
	label: 'All'
}]

Table

The third option would be table, which is simply the name of table from which the records will be retrieved. Unlike the perspective and state options, the table option is actually shared with the Data Table from URL Definition widget. With our recent modification, the Data Table from URL Definition widget will now accept three URL parameters: table, filter, and fields. The table value will come directly from the table selected on our new companion widget. The filter and fields value will come from the filter and fields values defined in the configuration for the selected perspective, state, and table selected. For each defined perspective, there will be a configuration for one or more tables. For each of the specified tables, there will be filter and fields values for each defined state. To build the URL for our new page containing both the Data Table from URL Definition widget and our new content controller widget, we will use the selected perspective, state, and table to add URL parameters for perspective, state, and table directly from the selections, plus filter and fields, determined based on the values in the configuration file for the selected perspective, state, and table.

All together, including a couple of table definitions for the Requester and another for the Fulfiller, the complete example configuration looks like this:

var MyDataConfig = Class.create();
MyDataConfig.prototype = {

	initialize: function() {
	},

	perspective: [{
		name: 'requester',
		label: 'Requester',
		roles: ''
	},{
		name: 'fulfiller',
		label: 'Fulfiller',
		roles: 'itil'
	}],

	state: [{
		name: 'open',
		label: 'Open'
	},{
		name: 'closed',
		label: 'Closed'
	},{
		name: 'recent',
		label: 'Recent'
	},{
		name: 'all',
		label: 'All'
	}],

	table: {
		requester: [{
			name: 'incident',
			open: {
				filter: 'caller_idDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Eactive%3Dtrue',
				fields: 'number,opened_by,opened_at,short_description'
			},
			closed: {
				filter: 'caller_idDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Eactive%3Dfalse',
				fields: 'number,opened_by,opened_at,closed_at,short_description'
			},
			recent: {
				filter: 'caller_idDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Esys_created_on%3E%3Djavascript%3Ags.beginningOfLast30Days()',
				fields: 'number,state,opened_by,opened_at,closed_at,short_description'
			},
			all: {
				filter: 'caller_idDYNAMIC90d1921e5f510100a9ad2572f2b477fe',
				fields: 'number,state,opened_by,opened_at,closed_at,short_description'
			}
		},{
			name: 'sc_request',
			open: {
				filter: 'requested_forDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Eactive%3Dtrue',
				fields: 'number,opened_by,opened_at,short_description'
			},
			closed: {
				filter: 'requested_forDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Eactive%3Dfalse',
				fields: 'number,opened_by,opened_at,closed_at,short_description'
			},
			recent: {
				filter: 'requested_forDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Esys_created_on3E%3Djavascript%3Ags.beginningOfLast30Days()',
				fields: 'number,request_state,opened_by,opened_at,closed_at,short_description'
			},
			all: {
				filter: 'requested_forDYNAMIC90d1921e5f510100a9ad2572f2b477fe',
				fields: 'number,request_state,opened_by,opened_at,closed_at,short_description'
			}
		}],
		fulfiller: [{
			name: 'incident',
			open: {
				filter: 'assigned_toDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Eactive%3Dtrue',
				fields: 'number,caller_id,opened_at,short_description'
			},
			closed: {
				filter: 'assigned_toDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Eactive%3Dfalse',
				fields: 'number,caller_id,opened_at,closed_at,short_description'
			},
			recent: {
				filter: 'assigned_toDYNAMIC90d1921e5f510100a9ad2572f2b477fe%5Esys_created_on3E%3Djavascript%3Ags.beginningOfLast30Days()',
				fields: 'number,state,caller_id,opened_at,closed_at,short_description'
			},
			all: {
				filter: 'assigned_toDYNAMIC90d1921e5f510100a9ad2572f2b477fe',
				fields: 'number,state,caller_id,opened_at,closed_at,short_description'
			}
		}]
	},

	getConfig: function(sp) {
		return {
			perspective: this.perspective,
			state: this.state,
			table: this.table
		};
	},

	type: 'MyDataConfig'
};

Now that we have the configuration all figured out, all we have to do is actually build the widget! Well, that seems like a good exercise for the next time out