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