The task log user interface contains a variety of different components including date pickers and combo boxes. We will implement the UI by dividing the screen into three views. The outermost ManageTaskLogs
view will contain a toolbar and define a border layout to hold the TaskLogList
and TaskLogForm
views:
We have chosen the border
layout for this view to allow resizing of the TaskLogForm
view that is initially fixed to a width of 400px in the east
region. The ManageTaskLogs
definition is as follows:
Ext.define('TTT.view.tasklog.ManageTaskLogs', { extend: 'Ext.panel.Panel', xtype: 'managetasklogs', requires: ['Ext.toolbar.Toolbar', 'Ext.layout.container.Border', 'Ext.form.field.Date', 'TTT.view.tasklog.TaskLogList', 'TTT.view.tasklog.TaskLogForm'], layout: { type: 'border' }, initComponent: function() { var me = this; var now = new Date(); Ext.applyIf(me, { dockedItems: [{ xtype: 'toolbar', dock: 'top', items: [{ xtype: 'datefield', labelAlign: 'right', name: 'startDate', format: 'd-M-Y', fieldLabel: 'Start Date', value: Ext.Date.getFirstDateOfMonth(now), width: 180, labelWidth: 70 }, { xtype: 'datefield', labelAlign: 'right', name: 'endDate', format: 'd-M-Y', fieldLabel: 'End Date', value: Ext.Date.getLastDateOfMonth(now), width: 180, labelWidth: 70 }, { xtype: 'button', iconCls: 'search', itemId: 'searchBtn', text: 'Search' }, { xtype: 'button', iconCls: 'addnew', itemId: 'addTaskLogBtn', text: 'Add New' }] }], items: [{ xtype: 'taskloglist', region: 'center', margin: 1 }, { xtype: 'tasklogform', region: 'east', split: true, width: 400 }] }); me.callParent(arguments); } });
This class is defined in the view.tasklog
namespace. You will need to create the view/tasklog
sub directory before adding the ManageTaskLogs.js
file.
The date
fields are initialized with the start and end dates of the current month using the Ext.Date.getFirstDateOfMonth()
and Ext.Date.getLastDateOfMonth()
functions. Manipulating the dates is a common task in Ext JS 4 development, and there are many helpful functions in the Ext.Date
class that make such tasks easy.
The TaskLogList
view has been placed in the center
region of the border
layout, while the TaskLogForm
view has been given an initial fixed width of 400
in the east
region. This will ensure that larger screen resolutions scale the task log list to give a balanced view. A screen width of 1200px would hence show the following layout:
The border
layout also allows resizing of the TaskLogForm
view should the user wish to increase the width of the data entry fields.
The TaskLogForm
view is used to display a task log record:
Ext.define('TTT.view.tasklog.TaskLogForm', { extend: 'Ext.form.Panel', xtype: 'tasklogform', requires: ['Ext.form.FieldSet', 'Ext.form.field.ComboBox', 'Ext.form.field.Date', 'Ext.form.field.Number', 'Ext.form.field.TextArea', 'Ext.toolbar.Toolbar'], layout: { type: 'anchor' }, bodyPadding: 10, border: false, autoScroll: true, initComponent: function() { var me = this; Ext.applyIf(me, { items: [{ xtype: 'fieldset', hidden: true, padding: 10, fieldDefaults: { anchor: '100%' }, title: 'Task Log Entry', items: [{ xtype: 'combobox', name: 'project', fieldLabel: 'Project', queryMode: 'local', store: 'Project', valueField: 'idProject', listConfig: { minWidth: 300 }, tpl: Ext.create('Ext.XTemplate', '<tpl for=".">', '<div class="x-boundlist-item"><b>{companyName}</b>: {projectName}</div>', '</tpl>'), displayTpl: Ext.create('Ext.XTemplate', '<tpl for=".">', '{projectName}', '</tpl>') }, { xtype: 'combobox', name: 'idTask', fieldLabel: 'Task', displayField: 'taskName', queryMode: 'local', store: 'Task', valueField: 'idTask' }, { xtype: 'datefield', name: 'taskLogDate', format: 'd-M-Y', fieldLabel: 'Date' }, { xtype: 'numberfield', name: 'hours', minValue: 0, decimalPrecision: 2, itemId: 'taskHours', fieldLabel: 'Hours' }, { xtype: 'textareafield', height: 100, name: 'taskDescription', fieldLabel: 'Description', emptyText: 'Enter task log description here...' }, { xtype: 'toolbar', ui: 'footer', layout: { pack: 'end', type: 'hbox' }, items: [{ xtype: 'button', iconCls: 'delete', itemId: 'deleteBtn', disabled: true, text: 'Delete' }, { xtype: 'button', iconCls: 'save', itemId: 'saveBtn', text: 'Save' }] }] }] }); me.callParent(arguments); } });
The Project combobox defines two different templates: one for rendering the list and one for rendering the selected item text. The tpl
property combines the company name and project name for display in the dropdown:
When an item is selected, only the project name is shown as rendered by the displayTpl
template.
The TaskLogList
view is defined as:
Ext.define('TTT.view.tasklog.TaskLogList', { extend: 'Ext.grid.Panel', xtype: 'taskloglist', viewConfig: { markDirty: false, emptyText: 'There are no task log records to display...' }, title: 'Task Logs', store: 'TaskLog', requires: ['Ext.grid.feature.Summary', 'Ext.grid.column.Date', 'Ext.util.Point'], features: [{ ftype: 'summary', dock: 'bottom' }], initComponent: function() { var me = this; Ext.applyIf(me, { columns: [{ xtype: 'datecolumn', dataIndex: 'taskLogDate', format: 'd-M-Y', width: 80, text: 'Date' }, { xtype: 'gridcolumn', dataIndex: 'taskName', text: 'Task' }, { xtype: 'gridcolumn', dataIndex: 'taskDescription', flex: 1, text: 'Description', summaryType: 'count', summaryRenderer: function(value, summaryData, dataIndex) { return Ext.String.format('<div style="font-weight:bold;text-align:right;">{0} Records, Total Hours:</div>', value); } }, { xtype: 'gridcolumn', dataIndex: 'taskMinutes', width: 80, align: 'center', text: 'Hours', summaryType: 'sum', renderer: function(value, metaData, record) { return record.get('hours'), }, summaryRenderer: function(value, summaryData, dataIndex) { var valHours = value / 60; return Ext.String.format('<b>{0}</b>', valHours); } }] }); me.callParent(arguments); } });
The viewConfig
properties are used to create an instance of the Ext.grid.View
class, which provides a grid-specific view functionality. We will be performing updates on a per record basis, not by using batch updates via the store. The markDirty:false
property will ensure that the records saved successfully are not rendered with the dirty flag in the grid. If a task log search returns no records, the emptyText
value will be displayed in the grid to give the user immediate feedback.
The TaskLogList
view uses the summary
feature to display a total row containing the Records count and Total Hours displayed in the search listing. The summaryType
and summaryRender
definitions are used to configure the feature
displayed in the footer of the taskDescription
and taskMinutes
columns. The summary
value may be one of count
, sum
, min
, max
, or average
, of which we are using the count
and sum
values. More information about the summary
feature can be found at http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.grid.feature.Summary. The following screenshot displays the summary
feature in use:
There is also some code to note in the column representing the hours of work assigned to the task:
{ xtype: 'gridcolumn', dataIndex: 'taskMinutes', width:80, align:'center', text: 'Hours', summaryType:'sum', renderer:function(value, metaData, record){ return record.get('hours'), }, summaryRenderer: function(value, summaryData, dataIndex) { var valHours = value/60; return Ext.String.format('<b>{0}</b>', valHours); } }
The time worked per task log is stored in the database in minutes but displayed on the frontend as hours. The column is bound to the taskMinutes
field in the model. The renderer displays the (calculated) hours
field of the TaskLog
model (this will be defined in the section that follows). The summary
feature uses the taskMinutes
field to calculate the total time as the feature requires a real (not converted) model field to act on. This total time in minutes is then converted in the summaryRenderer
function to hours for display.
18.227.79.241