Previous Page
Next Page

12.12. Assign-Tickets Component

The ticket-assignment component provides the users of the ticket manager with a quick way to assign unassigned tickets to users in the system. This is done by dragging tickets from a table and dropping them on a box that represents each user. This interface is a good example of the power that fully interactive interfaces give users. A drag-and-drop approach is especially useful for tasks such as sorting or assigning, because they are made up of elements that are mappable and that are often time-consuming with pre-AJAX approaches. The scriptaculous library is used to provide the drag-and-drop logic in this example. The interface of the assign-tickets component is created by Ticket.php (see Listing 12-21) and is shown in Figure 12-15.

Figure 12-15. Assign-tickets component

Listing 12-21. Ticket.php

1  <div id="assign" class="section">
2  <table id="aTable" cellspacing="0">
3  <thead>
4    <tr>
5      <th>#</th>
6      <th>Title</th>
7      <th>Status</th>
8      <th>Last Change</th>
9      <th>Opened By</th>
10     <th>Assigned To</th>
11   </tr>
12 </thead>
13 <tbody>
14   <tr id="aTemplate">
15   <td><a href="javascript:viewTicket({$ticket_id});"
16     >{$ticket_id}</a></td>
17   <td>{$title}</td>
18   <td>{$status}</td>
19   <td>{$last_change}</td>
20   <td>{$username}</td>
21   <td>{$assigned_to}</td>
22   </tr>

23 </tbody>
24 </table>
26 <p>Drag assign from a ticket row to a
27   users box to assign</p>
28 <p id="dragMessage"></p>
29 <div id="assignUsers">
30 </div>
31 </div>

The Ticket.php file provides the interface for assigning tickets to users. This interface is made of two parts: The first is a table that lists unassigned tickets, and the second is a box containing boxes for each user. The table is defined on lines 224 and follows the standard, dynamic-table model we've used throughout the application. It contains a thead tag (lines 312), which contains the headings for the table, and a tbody tag, which contains a single row (lines 1422). This row acts as the template for the buildTable and updateRow functions in Ticket.js. The file also contains a DIV (lines 2930) to hold the drop targets for the ticket rows. This target is empty now because the targets will be built dynamically from the list of users. The JavaScript that builds the user drop targets and powers the rest of the interface is shown in Listing 12-22.

Listing 12-22. Assign.js

1  function aOnDisplay() {
2    rAssign.listUsers();
3    rAssign.listUnassignedTickets();
4  }
6  function aSetup() {
7    app.templates['aTable'] =
8      byId('aTemplate').cloneNode(true);
9    byId('assign').onDisplay = aOnDisplay;
10 }
11 app.setup.push(aSetup);
13 var aCallback = {
14   listUnassignedTickets: function(result) {
15     buildTable(result,'aTable');
16     makeDraggable();
17   },
18   listUsers: function(result) {
19     buildUserDrops(result);
20   }
21 }
22 var rAssign = new Ticket(aCallback);
24   function makeDraggable() {
25   var tbody = byId('aTable').tBodies[0];
26   for(var i = 0; i < tbody.rows.length; i++) {
27     var td = document.createElement('td');
28     var div = document.createElement('div');
29     div.innerHTML = 'drag';
30     div.className = 'handle'
31     td.appendChild(div);
32     tbody.rows[i].appendChild(td);
34     new Draggable(div,
35       {revert:true, ghosting: false});
36   }
37 }
39 function buildUserDrops(users) {
40   byId('assignUsers').innerHTML = '';
41   for(var i in users) {
42     var div = document.createElement('div');
43     div.className = 'user';
44     div.innerHTML = users[i];
45     div.userId = i;
47     byId('assignUsers').appendChild(div);
48     Droppables.add(div,{
49     accept: 'handle',
50     hoverclass: 'highlight',
51     onDrop: function(element, u) {
52       assignTicket(element,u);
53     }
54     });
55   }
56 }
58 function assignTicket(handle,user) {
59   var row = handle.parentNode.parentNode;
60   var title = row.cells[1].innerHTML;
61   var id = row.cells[0].firstChild.innerHTML;
62   rAssign.assignTicket(id,user.userId);
64   byId('dragMessage').innerHTML = "Assigned ticket #"+
65     id+" '"+title+"' to "+user.innerHTML;
67   row.cells[5].innerHTML = user.innerHTML;
68   new Effect.Highlight(row);
69 }

The ticket-assignment component uses both a setup function and an onDisplay function, so it starts by setting those up. The onDisplay function (lines 14) is run each time the user loads the section. In this function, AJAX calls are made to the server to load the users and unassigned tickets in the browser. The ticket information is updated when the section is displayed because you don't want to be in a situation in which you are reassigning tickets that someone else already assigned. This problem can happen if you are working with stale data; updating the data on section display least guarantees you fresh data at the start of the assignment process. The Setup function (lines 69) loads the template row into the app object using cloneNode so that it can be used latereven after the source is overwritten. The Setup function also registers the onDisplay function because that can't be done until the assign DIV is loaded.

The next ten lines take care of setting up the AJAX class. Callbacks for listUnassignedTickets and listUsers are registered. As in the rest of the application, these callbacks do minimal work, leaving the implementation to functions that are registered later in the file.

The rest of Assign.js contains the functions that provide the functionality of the component. MakeDraggable (lines 2437) makes the rows in the ticket table draggable. It does this by looping over the rows in the body of the table and adding a new td on the right side. This td will contain a div, which is the drag handle for the row. The use of the handle DIV makes it clear how you should interact with the table; it works better than directly dragging-and-dropping the table rows because the rows have a variety of bugs in different browsers and they don't always offer a great user experience. The DIV is made draggable on lines 3435 by creating a new scriptaculous Draggable instance; the revert option is set to true so that the DIV stays in place after it is dragged to a user's drop target.

The buildUserDrops function takes a list of users that is returned from the server and dynamically creates a DIV for each user that is used as a drop target for the ticket rows. This process starts by clearing the target DIV (line 40) because drops may already be in place from an earlier view of this section. Then the list of users is iterated over. On lines 4245, a DIVis created, and it's populated with some of the user's information. Included in this is the user's user_id set as a property on the DIV so that it can be accessed by other code when the tickets are dropped. The function finishes by adding the new DIV to the target area (line 47) and making it a drop target (lines 4854). When we make it a drop target, we set an onDrop handler. This handler calls the assignTicket function and gives us a way to tie the drop event from the user interface to the back end.

The assignTicket function runs when a drop is completed. The first parameter, handle, is the DIV from the ticket row; the second parameter, user, is the user's drop target DIV. The function starts by using the handle's parentNode property to get the actual table row (line 59), and then it uses the row to get the title of the ticket (line 60) and the ticket's ID (line 61). With that information, an AJAX call to the server can be made. This call assigns the ticket to the specified users (line 62). The rest of the function provides status feedback to the user; lines 6465 provide a textual message, and lines 6768 update the assigned column in the ticket table and highlight the row to show that it's been updated.

Previous Page
Next Page