]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | Ext.require([\r |
2 | '*'\r | |
3 | ]);\r | |
4 | \r | |
5 | Ext.BLANK_IMAGE_URL = '../libs/ext-4.0/resources/themes/images/default/tree/s.gif';\r | |
6 | \r | |
7 | Ext.onReady(function() {\r | |
8 | \r | |
9 | // Employee Data Model\r | |
10 | Ext.regModel('Employee', {\r | |
11 | fields: [\r | |
12 | {name:'id', type:'int'},\r | |
13 | {name:'first_name', type:'string'},\r | |
14 | {name:'last_name', type:'string'},\r | |
15 | {name:'title', type:'string'}\r | |
16 | ],\r | |
17 | \r | |
18 | hasMany: {model:'Review', name:'reviews'}\r | |
19 | });\r | |
20 | \r | |
21 | // Review Data Model \r | |
22 | Ext.regModel('Review', {\r | |
23 | fields: [\r | |
24 | {name:'review_date', label:'Date', type:'date', dateFormat:'d-m-Y'},\r | |
25 | {name:'attendance', label:'Attendance', type:'int'},\r | |
26 | {name:'attitude', label:'Attitude', type:'int'},\r | |
27 | {name:'communication', label:'Communication', type:'int'},\r | |
28 | {name:'excellence', label:'Excellence', type:'int'},\r | |
29 | {name:'skills', label:'Skills', type:'int'},\r | |
30 | {name:'teamwork', label:'Teamwork', type:'int'},\r | |
31 | {name:'employee_id', label:'Employee ID', type:'int'}\r | |
32 | ],\r | |
33 | \r | |
34 | belongsTo: 'Employee'\r | |
35 | });\r | |
36 | \r | |
37 | // Instance of a Data Store to hold Employee records\r | |
38 | var employeeStore = new Ext.data.Store({\r | |
39 | storeId:'employeeStore',\r | |
40 | model:'Employee',\r | |
41 | data:[\r | |
42 | {id:1, first_name:'Michael', last_name:'Scott', title:'Regional Manager'},\r | |
43 | {id:2, first_name:'Dwight', last_name:'Schrute', title:'Sales Rep'},\r | |
44 | {id:3, first_name:'Jim', last_name:'Halpert', title:'Sales Rep'},\r | |
45 | {id:4, first_name:'Pam', last_name:'Halpert', title:'Office Administrator'},\r | |
46 | {id:5, first_name:'Andy', last_name:'Bernard', title:'Sales Rep'},\r | |
47 | {id:6, first_name:'Stanley', last_name:'Hudson', title:'Sales Rep'},\r | |
48 | {id:7, first_name:'Phyllis', last_name:'Lapin-Vance', title:'Sales Rep'},\r | |
49 | {id:8, first_name:'Kevin', last_name:'Malone', title:'Accountant'},\r | |
50 | {id:9, first_name:'Angela', last_name:'Martin', title:'Senior Accountant'}, \r | |
51 | {id:10, first_name:'Meredith', last_name:'Palmer', title:'Supplier Relations Rep'} \r | |
52 | ],\r | |
53 | autoLoad:true \r | |
54 | }); \r | |
55 | \r | |
56 | /**\r | |
57 | * App.RadarStore\r | |
58 | * @extends Ext.data.Store\r | |
59 | * This is a specialized Data Store with dynamically generated fields\r | |
60 | * data reformating capabilities to transform Employee and Review data\r | |
61 | * into the format required by the Radar Chart.\r | |
62 | *\r | |
63 | * The constructor demonstrates dynamically generating store fields.\r | |
64 | * populateReviewScores() populates the store using records from \r | |
65 | * the reviewStore which holds all the employee review scores.\r | |
66 | *\r | |
67 | * calculateAverageScores() iterates through each metric in the\r | |
68 | * review and calculates an average across all available reviews.\r | |
69 | * \r | |
70 | * Most of the actual data population and updates done by\r | |
71 | * addUpdateRecordFromReviews() and removeRecordFromReviews()\r | |
72 | * called when add/update/delete events are triggered on the ReviewStore. \r | |
73 | */ \r | |
74 | Ext.define('App.RadarStore', {\r | |
75 | extend: 'Ext.data.Store',\r | |
76 | \r | |
77 | constructor: function(config) {\r | |
78 | config = config || {};\r | |
79 | var dynamicFields = ['metric', 'avg']; // initalize the non-dynamic fields first\r | |
80 | \r | |
81 | employeeStore.each(function(record){ // loops through all the employees to setup the dynamic fields\r | |
82 | dynamicFields.push('eid_' + record.get('id'));\r | |
83 | });\r | |
84 | \r | |
85 | Ext.apply(config, {\r | |
86 | storeId:'radarStore', // let's us look it up later using Ext.data.StoreMgr.lookup('radarStore')\r | |
87 | fields:dynamicFields,\r | |
88 | data:[]\r | |
89 | });\r | |
90 | \r | |
91 | App.RadarStore.superclass.constructor.call(this, config);\r | |
92 | },\r | |
93 | \r | |
94 | addUpdateRecordFromReviews: function(reviews) {\r | |
95 | var me = this;\r | |
96 | \r | |
97 | Ext.Array.each(reviews, function(review, recordIndex, all) { // add a new radarStore record for each review record \r | |
98 | var eid = 'eid_' + review.get('employee_id'); // creates a unique id for each employee column in the store \r | |
99 | \r | |
100 | review.fields.each(function(field) {\r | |
101 | \r | |
102 | if(field.name !== "employee_id" && field.name !== "review_date") { // filter out the fields we don't need\r | |
103 | var metricRecord = me.findRecord('metric', field.name); // checks for an existing metric record in the store\r | |
104 | if(metricRecord) {\r | |
105 | metricRecord.set(eid, review.get(field.name)); // updates existing record with field value from review\r | |
106 | } else {\r | |
107 | var newRecord = {}; // creates a new object we can populate with dynamic keys and values to create a new record\r | |
108 | newRecord[eid] = review.get(field.name);\r | |
109 | newRecord['metric'] = field.label;\r | |
110 | me.add(newRecord);\r | |
111 | }\r | |
112 | }\r | |
113 | });\r | |
114 | });\r | |
115 | \r | |
116 | this.calculateAverageScores(); // update average scores\r | |
117 | },\r | |
118 | \r | |
119 | /**\r | |
120 | * Calculates an average for each metric across all employees.\r | |
121 | * We use this to create the average series always shown in the Radar Chart. \r | |
122 | */ \r | |
123 | calculateAverageScores: function() {\r | |
124 | var me = this; // keeps the store in scope during Ext.Array.each\r | |
125 | var reviewStore = Ext.data.StoreMgr.lookup('reviewStore');\r | |
126 | \r | |
127 | var Review = Ext.ModelMgr.getModel('Review');\r | |
128 | \r | |
129 | Ext.Array.each(Review.prototype.fields.keys, function(fieldName) { // loop through the Review model fields and calculate average scores\r | |
130 | if(fieldName !== "employee_id" && fieldName !== "review_date") { // ignore non-score fields\r | |
131 | var avgScore = Math.round(reviewStore.average(fieldName)); // takes advantage of Ext.data.Store.average()\r | |
132 | var record = me.findRecord('metric', fieldName);\r | |
133 | \r | |
134 | if(record) {\r | |
135 | record.set('avg', avgScore);\r | |
136 | } else {\r | |
137 | me.add({metric:fieldName, avg:avgScore});\r | |
138 | }\r | |
139 | }\r | |
140 | });\r | |
141 | },\r | |
142 | \r | |
143 | populateReviewScores: function() {\r | |
144 | var reviewStore = Ext.data.StoreMgr.lookup('reviewStore');\r | |
145 | this.addUpdateRecordFromReviews(reviewStore.data.items); // add all the review records to this store\r | |
146 | },\r | |
147 | \r | |
148 | removeRecordFromReviews: function(reviews) {\r | |
149 | var me = this;\r | |
150 | Ext.Array.each(reviews, function(review, recordIndex, all) {\r | |
151 | var eid = 'eid_' + review.get('employee_id');\r | |
152 | \r | |
153 | me.each(function(record) {\r | |
154 | delete record.data[eid];\r | |
155 | });\r | |
156 | });\r | |
157 | \r | |
158 | // upate average scores\r | |
159 | this.calculateAverageScores(); \r | |
160 | }\r | |
161 | }); // end App.RadarStore definition\r | |
162 | \r | |
163 | \r | |
164 | /** Creates an instance of App.RadarStore here so we\r | |
165 | * here so we can re-use it during the life of the app.\r | |
166 | * Otherwise we'd have to create a new instance everytime\r | |
167 | * refreshRadarChart() is run.\r | |
168 | */\r | |
169 | var radarStore = new App.RadarStore();\r | |
170 | \r | |
171 | var reviewStore = new Ext.data.Store({\r | |
172 | storeId:'reviewStore',\r | |
173 | model:'Review',\r | |
174 | data:[\r | |
175 | {review_date:'01-04-2011', attendance:10, attitude:6, communication:6, excellence:3, skills:3, teamwork:3, employee_id:1},\r | |
176 | {review_date:'01-04-2011', attendance:6, attitude:5, communication:2, excellence:8, skills:9, teamwork:5, employee_id:2},\r | |
177 | {review_date:'01-04-2011', attendance:5, attitude:4, communication:3, excellence:5, skills:6, teamwork:2, employee_id:3},\r | |
178 | {review_date:'01-04-2011', attendance:8, attitude:2, communication:4, excellence:2, skills:5, teamwork:6, employee_id:4},\r | |
179 | {review_date:'01-04-2011', attendance:4, attitude:1, communication:5, excellence:7, skills:5, teamwork:5, employee_id:5},\r | |
180 | {review_date:'01-04-2011', attendance:5, attitude:2, communication:4, excellence:7, skills:9, teamwork:8, employee_id:6},\r | |
181 | {review_date:'01-04-2011', attendance:10, attitude:7, communication:8, excellence:7, skills:3, teamwork:4, employee_id:7}, \r | |
182 | {review_date:'01-04-2011', attendance:10, attitude:8, communication:8, excellence:4, skills:8, teamwork:7, employee_id:8},\r | |
183 | {review_date:'01-04-2011', attendance:6, attitude:4, communication:9, excellence:7, skills:6, teamwork:5, employee_id:9},\r | |
184 | {review_date:'01-04-2011', attendance:7, attitude:5, communication:9, excellence:4, skills:2, teamwork:4, employee_id:10} \r | |
185 | ],\r | |
186 | listeners: {\r | |
187 | add:function(store, records, storeIndex) {\r | |
188 | var radarStore = Ext.data.StoreMgr.lookup('radarStore');\r | |
189 | \r | |
190 | if(radarStore) { // only add records if an instance of the rardarStore already exists\r | |
191 | radarStore.addUpdateRecordFromReviews(records); // add a new radarStore records for new review records \r | |
192 | }\r | |
193 | }, // end add listener\r | |
194 | update: function(store, record, operation) {\r | |
195 | radarStore.addUpdateRecordFromReviews([record]);\r | |
196 | refreshRadarChart();\r | |
197 | },\r | |
198 | remove: function(store, records, storeIndex) {\r | |
199 | // update the radarStore and regenerate the radarChart\r | |
200 | Ext.data.StoreMgr.lookup('radarStore').removeRecordFromReviews(records);\r | |
201 | refreshRadarChart();\r | |
202 | } // end remove listener\r | |
203 | }\r | |
204 | });\r | |
205 | \r | |
206 | /**\r | |
207 | * App.PerformanceRadar\r | |
208 | * @extends Ext.chart.Chart\r | |
209 | * This is a specialized Radar Chart which we use to display employee \r | |
210 | * performance reviews.\r | |
211 | *\r | |
212 | * The class will be registered with an xtype of 'performanceradar'\r | |
213 | */ \r | |
214 | Ext.define('App.PerformanceRadar', {\r | |
215 | extend: 'Ext.chart.Chart',\r | |
216 | alias: 'widget.performanceradar', // register xtype performanceradar\r | |
217 | constructor: function(config) {\r | |
218 | config = config || {};\r | |
219 | \r | |
220 | this.setAverageSeries(config); // make sure average is always present\r | |
221 | \r | |
222 | Ext.apply(config, {\r | |
223 | id:'radarchart',\r | |
224 | theme:'Category2',\r | |
225 | animate:true,\r | |
226 | store: Ext.data.StoreMgr.lookup('radarStore'),\r | |
227 | margin:'0 0 50 0',\r | |
228 | width:350,\r | |
229 | height:500,\r | |
230 | insetPadding:80,\r | |
231 | legend:{\r | |
232 | position: 'bottom'\r | |
233 | },\r | |
234 | axes: [{\r | |
235 | type:'Radial',\r | |
236 | position:'radial',\r | |
237 | label:{\r | |
238 | display: true\r | |
239 | }\r | |
240 | }]\r | |
241 | }); // end Ext.apply\r | |
242 | \r | |
243 | App.PerformanceRadar.superclass.constructor.call(this, config);\r | |
244 | \r | |
245 | }, // end constructor\r | |
246 | \r | |
247 | setAverageSeries: function(config) {\r | |
248 | var avgSeries = {\r | |
249 | type: 'radar',\r | |
250 | xField: 'metric',\r | |
251 | yField: 'avg',\r | |
252 | title: 'Avg',\r | |
253 | labelDisplay:'over',\r | |
254 | showInLegend: true,\r | |
255 | showMarkers: true,\r | |
256 | markerCfg: {\r | |
257 | radius: 5,\r | |
258 | size: 5,\r | |
259 | stroke:'#0677BD',\r | |
260 | fill:'#0677BD'\r | |
261 | },\r | |
262 | style: {\r | |
263 | 'stroke-width': 2,\r | |
264 | 'stroke':'#0677BD',\r | |
265 | fill: 'none'\r | |
266 | }\r | |
267 | };\r | |
268 | \r | |
269 | if(config.series) { \r | |
270 | config.series.push(avgSeries); // if a series is passed in then append the average to it\r | |
271 | } else { \r | |
272 | config.series = [avgSeries]; // if a series isn't passed just create average\r | |
273 | }\r | |
274 | } \r | |
275 | \r | |
276 | }); // end Ext.ux.Performance radar definition\r | |
277 | \r | |
278 | /**\r | |
279 | * App.EmployeeDetail\r | |
280 | * @extends Ext.Panel\r | |
281 | * This is a specialized Panel which is used to show information about\r | |
282 | * an employee and the reviews we have on record for them.\r | |
283 | *\r | |
284 | * This demonstrates adding 2 custom properties (tplMarkup and\r | |
285 | * startingMarkup) to the class. It also overrides the initComponent\r | |
286 | * method and adds a new method called updateDetail.\r | |
287 | *\r | |
288 | * The class will be registered with an xtype of 'employeedetail'\r | |
289 | */\r | |
290 | Ext.define('App.EmployeeDetail', {\r | |
291 | extend: 'Ext.panel.Panel',\r | |
292 | // register the App.EmployeeDetail class with an xtype of employeedetail\r | |
293 | alias: 'widget.employeedetail',\r | |
294 | // add tplMarkup as a new property\r | |
295 | tplMarkup: [\r | |
296 | '<b>{first_name} {last_name}</b> ',\r | |
297 | 'Title: {title}<br/><br/>',\r | |
298 | '<b>Last Review</b> ',\r | |
299 | 'Attendance: {attendance} ',\r | |
300 | 'Attitude: {attitude} ',\r | |
301 | 'Communication: {communication} ',\r | |
302 | 'Excellence: {excellence} ',\r | |
303 | 'Skills: {skills} ',\r | |
304 | 'Teamwork: {teamwork}' \r | |
305 | ],\r | |
306 | \r | |
307 | height:90,\r | |
308 | bodyPadding: 7,\r | |
309 | // override initComponent to create and compile the template\r | |
310 | // apply styles to the body of the panel\r | |
311 | initComponent: function() {\r | |
312 | this.tpl = new Ext.Template(this.tplMarkup);\r | |
313 | \r | |
314 | // call the superclass's initComponent implementation\r | |
315 | App.EmployeeDetail.superclass.initComponent.call(this);\r | |
316 | }\r | |
317 | });\r | |
318 | \r | |
319 | Ext.define('App.ReviewWindow', {\r | |
320 | extend: 'Ext.window.Window',\r | |
321 | \r | |
322 | constructor: function(config) { \r | |
323 | config = config || {};\r | |
324 | Ext.apply(config, { \r | |
325 | title:'Employee Performance Review',\r | |
326 | width:320,\r | |
327 | height:420,\r | |
328 | layout:'fit', \r | |
329 | items:[{\r | |
330 | xtype:'form',\r | |
331 | id:'employeereviewcomboform',\r | |
332 | fieldDefaults: {\r | |
333 | labelAlign: 'left',\r | |
334 | labelWidth: 90,\r | |
335 | anchor: '100%'\r | |
336 | }, \r | |
337 | bodyPadding:5,\r | |
338 | items:[{\r | |
339 | xtype:'fieldset',\r | |
340 | title:'Employee Info',\r | |
341 | items:[{\r | |
342 | xtype:'hiddenfield',\r | |
343 | name:'employee_id'\r | |
344 | },{\r | |
345 | xtype:'textfield',\r | |
346 | name:'first_name',\r | |
347 | fieldLabel:'First Name',\r | |
348 | allowBlank:false\r | |
349 | },{\r | |
350 | xtype:'textfield',\r | |
351 | name:'last_name',\r | |
352 | fieldLabel:'Last Name',\r | |
353 | allowBlank:false \r | |
354 | },{\r | |
355 | xtype:'textfield',\r | |
356 | name:'title',\r | |
357 | fieldLabel:'Title',\r | |
358 | allowBlank:false \r | |
359 | }]\r | |
360 | },{\r | |
361 | xtype:'fieldset',\r | |
362 | title:'Performance Review',\r | |
363 | items:[{\r | |
364 | xtype:'datefield',\r | |
365 | name:'review_date',\r | |
366 | fieldLabel:'Review Date',\r | |
367 | format:'d-m-Y', \r | |
368 | maxValue: new Date(),\r | |
369 | value: new Date(),\r | |
370 | allowBlank:false\r | |
371 | },{\r | |
372 | xtype:'slider',\r | |
373 | name:'attendance',\r | |
374 | fieldLabel:'Attendance', \r | |
375 | value:5,\r | |
376 | increment:1,\r | |
377 | minValue:1,\r | |
378 | maxValue:10\r | |
379 | },{\r | |
380 | xtype:'slider',\r | |
381 | name:'attitude',\r | |
382 | fieldLabel:'Attitude',\r | |
383 | value:5,\r | |
384 | minValue: 1,\r | |
385 | maxValue: 10\r | |
386 | },{\r | |
387 | xtype:'slider',\r | |
388 | name:'communication',\r | |
389 | fieldLabel:'Communication', \r | |
390 | value:5,\r | |
391 | increment:1,\r | |
392 | minValue:1,\r | |
393 | maxValue:10\r | |
394 | },{\r | |
395 | xtype:'numberfield',\r | |
396 | name:'excellence',\r | |
397 | fieldLabel:'Excellence',\r | |
398 | value:5,\r | |
399 | minValue: 1,\r | |
400 | maxValue: 10 \r | |
401 | },{\r | |
402 | xtype:'numberfield',\r | |
403 | name:'skills',\r | |
404 | fieldLabel:'Skills',\r | |
405 | value:5,\r | |
406 | minValue: 1,\r | |
407 | maxValue: 10 \r | |
408 | },{\r | |
409 | xtype:'numberfield',\r | |
410 | name:'teamwork',\r | |
411 | fieldLabel:'Teamwork',\r | |
412 | value:5,\r | |
413 | minValue: 1,\r | |
414 | maxValue: 10 \r | |
415 | }]\r | |
416 | }]\r | |
417 | }],\r | |
418 | buttons:[{\r | |
419 | text:'Cancel',\r | |
420 | width:80,\r | |
421 | handler:function() {\r | |
422 | this.up('window').close();\r | |
423 | }\r | |
424 | },\r | |
425 | {\r | |
426 | text:'Save',\r | |
427 | width:80,\r | |
428 | handler:function(btn, eventObj) {\r | |
429 | var window = btn.up('window');\r | |
430 | var form = window.down('form').getForm();\r | |
431 | \r | |
432 | if (form.isValid()) {\r | |
433 | window.getEl().mask('saving data...');\r | |
434 | var vals = form.getValues();\r | |
435 | var employeeStore = Ext.data.StoreMgr.lookup('employeeStore');\r | |
436 | var currentEmployee = employeeStore.findRecord('id', vals['employee_id']);\r | |
437 | \r | |
438 | // look up id for this employee to see if they already exist\r | |
439 | if(vals['employee_id'] && currentEmployee) {\r | |
440 | currentEmployee.set('first_name', vals['first_name']);\r | |
441 | currentEmployee.set('last_name', vals['last_name']);\r | |
442 | currentEmployee.set('title', vals['title']);\r | |
443 | \r | |
444 | var currentReview = Ext.data.StoreMgr.lookup('reviewStore').findRecord('employee_id', vals['employee_id']);\r | |
445 | currentReview.set('review_date', vals['review_date']);\r | |
446 | currentReview.set('attendance', vals['attendance']);\r | |
447 | currentReview.set('attitude', vals['attitude']);\r | |
448 | currentReview.set('communication', vals['communication']);\r | |
449 | currentReview.set('excellence', vals['excellence']);\r | |
450 | currentReview.set('skills', vals['skills']);\r | |
451 | currentReview.set('teamwork', vals['teamwork']); \r | |
452 | } else {\r | |
453 | var newId = employeeStore.getCount() + 1; \r | |
454 | \r | |
455 | employeeStore.add({\r | |
456 | id: newId,\r | |
457 | first_name: vals['first_name'],\r | |
458 | last_name: vals['last_name'],\r | |
459 | title: vals['title']\r | |
460 | });\r | |
461 | \r | |
462 | Ext.data.StoreMgr.lookup('reviewStore').add({\r | |
463 | review_date: vals['review_date'],\r | |
464 | attendance: vals['attendance'],\r | |
465 | attitude: vals['attitude'],\r | |
466 | communication: vals['communication'],\r | |
467 | excellence: vals['excellence'],\r | |
468 | skills: vals['skills'],\r | |
469 | teamwork: vals['teamwork'],\r | |
470 | employee_id: newId\r | |
471 | });\r | |
472 | }\r | |
473 | window.getEl().unmask();\r | |
474 | window.close();\r | |
475 | }\r | |
476 | }\r | |
477 | }]\r | |
478 | }); // end Ext.apply\r | |
479 | \r | |
480 | App.ReviewWindow.superclass.constructor.call(this, config);\r | |
481 | \r | |
482 | } // end constructor\r | |
483 | \r | |
484 | });\r | |
485 | \r | |
486 | \r | |
487 | // adds a record to the radar chart store and \r | |
488 | // creates a series in the chart for selected employees\r | |
489 | function refreshRadarChart(employees) { \r | |
490 | employees = employees || []; // in case its called with nothing we'll at least have an empty array\r | |
491 | var existingRadarChart = Ext.getCmp('radarchart'); // grab the radar chart component (used down below)\r | |
492 | var reportsPanel = Ext.getCmp('reportspanel'); // grab the reports panel component (used down below)\r | |
493 | var dynamicSeries = []; // setup an array of chart series that we'll create dynamically\r | |
494 | \r | |
495 | for(var index = 0; index < employees.length; index++) {\r | |
496 | var fullName = employees[index].get('first_name') + ' ' + employees[index].get('last_name');\r | |
497 | var eid = 'eid_' + employees[index].get('id');\r | |
498 | \r | |
499 | // add to the dynamic series we're building\r | |
500 | dynamicSeries.push({\r | |
501 | type: 'radar',\r | |
502 | title: fullName,\r | |
503 | xField: 'metric',\r | |
504 | yField: eid,\r | |
505 | labelDisplay: 'over',\r | |
506 | showInLegend: true,\r | |
507 | showMarkers: true,\r | |
508 | markerCfg: {\r | |
509 | radius: 5,\r | |
510 | size: 5\r | |
511 | },\r | |
512 | style: {\r | |
513 | 'stroke-width': 2,\r | |
514 | fill: 'none'\r | |
515 | }\r | |
516 | });\r | |
517 | \r | |
518 | } // end for loop\r | |
519 | \r | |
520 | // destroy the existing chart\r | |
521 | existingRadarChart.destroy();\r | |
522 | // create the new chart using the dynamic series we just made\r | |
523 | var newRadarChart = new App.PerformanceRadar({series:dynamicSeries});\r | |
524 | // mask the panel while we switch out charts\r | |
525 | reportsPanel.getEl().mask('updating chart...');\r | |
526 | // display the new one\r | |
527 | reportsPanel.add(newRadarChart);\r | |
528 | // un mask the reports panel\r | |
529 | reportsPanel.getEl().unmask();\r | |
530 | }\r | |
531 | \r | |
532 | function refreshEmployeeDetails(employees) {\r | |
533 | var detailsPanel = Ext.getCmp('detailspanel');\r | |
534 | var reviewStore = Ext.data.StoreMgr.lookup('reviewStore');\r | |
535 | var items = [];\r | |
536 | \r | |
537 | for(var index = 0; index < employees.length; index++) {\r | |
538 | var templateData = Ext.applyIf(employees[index].data, reviewStore.findRecord('employee_id', employees[index].get('id')).data);\r | |
539 | var employeePanel = new App.EmployeeDetail({\r | |
540 | title:employees[index].get('first_name') + ' ' + employees[index].get('last_name'),\r | |
541 | data:templateData // combined employee and latest review dataTransfer\r | |
542 | });\r | |
543 | items.push(employeePanel);\r | |
544 | }\r | |
545 | \r | |
546 | detailsPanel.getEl().mask('updating details...');\r | |
547 | detailsPanel.removeAll();\r | |
548 | detailsPanel.add(items);\r | |
549 | detailsPanel.getEl().unmask();\r | |
550 | }\r | |
551 | \r | |
552 | // sets Up Checkbox Selection Model for the Employee Grid\r | |
553 | var checkboxSelModel = new Ext.selection.CheckboxModel();\r | |
554 | \r | |
555 | var viewport = new Ext.container.Viewport({\r | |
556 | id:'mainviewport',\r | |
557 | layout: 'border', // sets up Ext.layout.container.Border\r | |
558 | items: [{\r | |
559 | xtype:'panel',\r | |
560 | region:'center',\r | |
561 | layout:'auto',\r | |
562 | scrollable:true,\r | |
563 | title:'Employee Performance Manager',\r | |
564 | tbar:[{\r | |
565 | text:'Add Employee',\r | |
566 | tooltip:'Add a new employee',\r | |
567 | iconCls:'add',\r | |
568 | handler:function() { // display a window to add a new employee\r | |
569 | new App.ReviewWindow().show();\r | |
570 | }\r | |
571 | }],\r | |
572 | items:[{\r | |
573 | xtype:'grid',\r | |
574 | store:Ext.data.StoreMgr.lookup('employeeStore'),\r | |
575 | height:300,\r | |
576 | columns:[{\r | |
577 | text:'First Name',\r | |
578 | dataIndex:'first_name',\r | |
579 | flex:2\r | |
580 | },\r | |
581 | {\r | |
582 | text:'Last Name',\r | |
583 | dataIndex:'last_name',\r | |
584 | flex:2\r | |
585 | },\r | |
586 | {\r | |
587 | text:'Title',\r | |
588 | dataIndex:'title',\r | |
589 | flex:3\r | |
590 | },\r | |
591 | {\r | |
592 | xtype:'actioncolumn',\r | |
593 | width:45,\r | |
594 | items:[{\r | |
595 | icon:'images/edit.png',\r | |
596 | tooltip:'Edit Employee',\r | |
597 | handler:function(grid, rowIndex, colIndex) {\r | |
598 | var employee = grid.getStore().getAt(rowIndex);\r | |
599 | var review = reviewStore.findRecord('employee_id', employee.get('id'));\r | |
600 | var win = new App.ReviewWindow({hidden:true});\r | |
601 | var form = win.down('form').getForm();\r | |
602 | form.loadRecord(employee);\r | |
603 | form.loadRecord(review);\r | |
604 | win.show();\r | |
605 | }\r | |
606 | },\r | |
607 | {\r | |
608 | icon:'images/delete.png',\r | |
609 | tooltip:'Delete Employee',\r | |
610 | width:75,\r | |
611 | handler:function(grid, rowIndex, colIndex) {\r | |
612 | Ext.Msg.confirm('Remove Employee?', 'Are you sure you want to remove this employee?',\r | |
613 | function(choice) {\r | |
614 | if(choice === 'yes') {\r | |
615 | var reviewStore = Ext.data.StoreMgr.lookup('reviewStore');\r | |
616 | \r | |
617 | var employee = grid.getStore().getAt(rowIndex);\r | |
618 | var reviewIndex = reviewStore.find('employee_id', employee.get('id'));\r | |
619 | reviewStore.removeAt(reviewIndex);\r | |
620 | grid.getStore().removeAt(rowIndex);\r | |
621 | }\r | |
622 | }\r | |
623 | ); \r | |
624 | }\r | |
625 | }]\r | |
626 | }],\r | |
627 | selModel: new Ext.selection.CheckboxModel(),\r | |
628 | columnLines: true,\r | |
629 | viewConfig: {stripeRows:true},\r | |
630 | listeners:{\r | |
631 | selectionchange:function(selModel, selected) {\r | |
632 | refreshRadarChart(selected);\r | |
633 | refreshEmployeeDetails(selected);\r | |
634 | }\r | |
635 | }\r | |
636 | },{\r | |
637 | xtype:'container',\r | |
638 | id:'detailspanel',\r | |
639 | layout:{\r | |
640 | type:'vbox',\r | |
641 | align:'stretch',\r | |
642 | autoSize:true\r | |
643 | }\r | |
644 | }]\r | |
645 | },{\r | |
646 | xtype:'panel', // sets up the chart panel (starts collapsed)\r | |
647 | region:'east',\r | |
648 | id:'reportspanel',\r | |
649 | title:'Performance Report',\r | |
650 | width:350,\r | |
651 | layout: 'fit',\r | |
652 | items:[{\r | |
653 | xtype:'performanceradar' // this instantiates a App.PerformanceRadar object\r | |
654 | }]\r | |
655 | }] // mainviewport items array ends here\r | |
656 | });\r | |
657 | \r | |
658 | }); |