task-manager 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -90,6 +90,64 @@ API文档](http://rdoc.info/github/menglifang/task-manager/master/TaskManager/Ap
90
90
  /*= require task-manager/extjs */
91
91
  ```
92
92
 
93
+ 在`app/assets/javascripts/extjs/app.js`文件中`controllers`添加:
94
+
95
+ ```javascript
96
+ controllers: [..., 'TM.controller.Plans', 'TM.controller.Tasks'];
97
+ ```
98
+
99
+ #### 后台
100
+
101
+ ##### Ruby Project
102
+
103
+ * 在`app/controllers/task_manager`下创建`AssigneesController.rb`文件:
104
+
105
+ 示例:
106
+
107
+ ```ruby
108
+ # -*- encoding: utf-8 -*-
109
+ module TaskManager
110
+ class AssigneesController < ApplicationController
111
+ respond_to :json
112
+
113
+ def index
114
+ departments = Department.all
115
+ assignees = departments.inject([]) do |c, i|
116
+ c << {
117
+ id: i.id,
118
+ parent_id: i.parent_id,
119
+ name: i.name,
120
+ class_name: i.class.name
121
+ }
122
+ end
123
+ # 或者使用如下方式
124
+ # assignees = departments.inject([]) { |c, i| c << i.as_json }
125
+
126
+ result = {
127
+ total: assignees.count,
128
+ assignees: assignees
129
+ }
130
+
131
+ render json: result, status: :ok
132
+ end
133
+ end
134
+ end
135
+ ```
136
+
137
+ * 在`config/routes.rb`文件中添加:
138
+
139
+ ```ruby
140
+ resources :assignees, only: [:index], module: 'TaskManager'
141
+ ```
142
+
143
+ 使用`rake
144
+ routes`命令,检查是否添加成功。如果成功,命令行中将会有如下输出:
145
+
146
+ ```
147
+ Routes for TaskManager::Engine:
148
+ assignees GET /assigneess(.:format) TaskManager/assignees#index {:format=>"json"}
149
+ ```
150
+
93
151
  ### 开发指南
94
152
 
95
153
  * 安装依赖包
@@ -9,6 +9,7 @@
9
9
  //= require_self
10
10
  //
11
11
  //= require ./extjs/app/view/assignee/TreeCombo
12
+ //= require ./extjs/app/view/callback/CheckboxCombo
12
13
  //= require ./extjs/app/view/task/Search
13
14
  //= require ./extjs/app/view/task/Grid
14
15
  //= require ./extjs/app/view/task/Index
@@ -24,6 +25,7 @@
24
25
  Ext.data.StoreManager.register(
25
26
  Ext.create('TM.store.Tasks', { storeId: 'TM.store.Tasks'}),
26
27
  Ext.create('TM.store.Assignees', { storeId: 'TM.store.Assignees'}),
28
+ Ext.create('TM.store.Callbacks', { storeId: 'TM.store.Callbacks'}),
27
29
  Ext.create('TM.store.WeekDays', { storeId: 'TM.store.WeekDays'}),
28
30
  Ext.create('TM.store.QuarterlyMonths', { storeId: 'TM.store.QuarterlyMonths'}),
29
31
  Ext.create('TM.store.Months', { storeId: 'TM.store.Months'}),
@@ -0,0 +1,34 @@
1
+ Ext.define('TM.model.Callback', {
2
+ extend: 'Ext.data.Model',
3
+
4
+ fields: [
5
+ { name: 'id', type: 'int' },
6
+ { name: 'name' },
7
+ { name: 'class_name' }
8
+ ],
9
+
10
+ proxy: {
11
+ type: 'rest',
12
+ url: '/callbacks',
13
+ reader: {
14
+ root: 'callbacks',
15
+ totalProperty: 'total'
16
+ }
17
+ },
18
+
19
+ getId: function(detailed) {
20
+ if(detailed) {
21
+ return this.get('class_name') + '-' + this.get('id');
22
+ } else {
23
+ return this.get('id');
24
+ }
25
+ },
26
+
27
+ getParentId: function(detailed) {
28
+ if(this.get('parent_id') && detailed) {
29
+ return this.get('class_name') + '-' + this.get('parent_id');
30
+ } else {
31
+ return this.get('parent_id');
32
+ }
33
+ }
34
+ });
@@ -19,6 +19,7 @@ Ext.define('TM.model.Plan', {
19
19
  { name: 'updated_at', type: 'date', persist: false },
20
20
 
21
21
  { name: 'assignees', type: 'auto', persist: false, defaultValue: [] },
22
+ { name: 'callbacks', type: 'auto', persist: false, defaultValue: [] },
22
23
  { name: 'assignables_attributes', type: 'auto', defaultValue: [] },
23
24
  { name: 'callables_attributes', type: 'auto', defaultValue: [] }
24
25
  ],
@@ -0,0 +1,6 @@
1
+ Ext.define('TM.store.Callbacks', {
2
+ extend: 'Ext.data.Store',
3
+
4
+ autoLoad: true,
5
+ model: 'TM.model.Callback'
6
+ });
@@ -1,6 +1,7 @@
1
1
  Ext.define('TM.view.assignee.TreeCombo', {
2
2
  xtype: 'assignee_treecombo',
3
3
  extend: 'Ext.ux.TreeCombo',
4
+ editable: false,
4
5
 
5
6
  setValue: function(valueInit) {
6
7
  if(typeof valueInit === 'object') {
@@ -38,7 +39,7 @@ Ext.define('TM.view.assignee.TreeCombo', {
38
39
  initComponent: function() {
39
40
  this.callParent(arguments);
40
41
 
41
- this.on('show', this.unCheckNodes);
42
+ this.on('render', this.unCheckNodes);
42
43
  },
43
44
 
44
45
  // @private
@@ -0,0 +1,17 @@
1
+ Ext.define('TM.view.callback.CheckboxCombo', {
2
+ extend: 'Ext.ux.form.CheckboxListCombo',
3
+ xtype: 'callback_checkboxcombo',
4
+
5
+ getSubmitValue: function() {
6
+ var callbacks = [];
7
+
8
+ Ext.Array.forEach(this.value, function(c, i) {
9
+ callbacks.push({
10
+ callback_id: c,
11
+ callback_type: Ext.getStore('TM.store.Callbacks').getAt(0).get('class_name')
12
+ });
13
+ });
14
+
15
+ return callbacks;
16
+ }
17
+ });
@@ -1,8 +1,11 @@
1
1
  Ext.define('TM.view.plan.Form', {
2
- requires: ['Ext.ux.TreeCombo'],
3
2
  extend: 'Ext.form.Panel',
4
3
  xtype: 'plan_form',
5
4
 
5
+ //requires: [
6
+ //'Ext.ux.TreeCombo',
7
+ //],
8
+
6
9
  defaultPlanType: 'yearly',
7
10
  defaultBeginToRemind: 0,
8
11
 
@@ -64,6 +67,15 @@ Ext.define('TM.view.plan.Form', {
64
67
  fieldLabel: '是否自动完成',
65
68
  xtype: 'checkbox',
66
69
  name: 'autocompletable'
70
+ }, {
71
+ fieldLabel: '超时回调',
72
+ xtype: 'callback_checkboxcombo',
73
+ editable: false,
74
+ name: 'callables_attributes',
75
+ store: 'TM.store.Callbacks',
76
+ multiSelect: true,
77
+ displayField: 'name',
78
+ valueField: 'id'
67
79
  }]
68
80
  }, {
69
81
  xtype: 'fieldset',
@@ -147,6 +159,7 @@ Ext.define('TM.view.plan.Form', {
147
159
  }, this);
148
160
 
149
161
  this.checkSelectedAssignees(record.get('assignees'));
162
+ this.checkSelectedCallbacks(record.get('callbacks'));
150
163
  },
151
164
 
152
165
  // @protected
@@ -163,6 +176,19 @@ Ext.define('TM.view.plan.Form', {
163
176
  this.refreshDeadline(this.defaultPlanType);
164
177
  },
165
178
 
179
+ // @private
180
+ checkSelectedCallbacks: function(callbacks) {
181
+ if (typeof callbacks === 'undefined') return;
182
+
183
+ var values = [];
184
+ callbacks.forEach(function(c) {
185
+ values.push(c.id);
186
+ });
187
+
188
+ //this.getCallbackCheckCombo().setValue(values.join(', '));
189
+ this.getCallbackCheckCombo().setValue(values);
190
+ },
191
+
166
192
  // @private
167
193
  checkSelectedAssignees: function(assignees) {
168
194
  if(typeof assignees === 'undefined') return;
@@ -184,7 +210,7 @@ Ext.define('TM.view.plan.Form', {
184
210
  }
185
211
  }, this);
186
212
 
187
- console.log(values);
213
+ //console.log(values);
188
214
  this.getAssigneesTreeCombo().setValue(values);
189
215
  },
190
216
 
@@ -286,5 +312,10 @@ Ext.define('TM.view.plan.Form', {
286
312
  // @private
287
313
  getAssigneesTreeCombo: function() {
288
314
  return this.query('assignee_treecombo')[0];
315
+ },
316
+
317
+ // @private
318
+ getCallbackCheckCombo: function() {
319
+ return this.query('callback_checkboxcombo')[0];
289
320
  }
290
321
  });
@@ -9,7 +9,7 @@ Ext.define('TM.view.plan.FormWindow', {
9
9
  modal: true,
10
10
 
11
11
  width: 600,
12
- height: 550,
12
+ height: 580,
13
13
 
14
14
  layout: {
15
15
  type: 'fit',
@@ -68,10 +68,14 @@ Ext.define('TM.view.plan.Grid', {
68
68
  return names.join(', ');
69
69
  },
70
70
  flex: 3
71
- //}, {
72
- //text: '最后任务生成时间',
73
- //dataIndex: 'last_task_created_at',
74
- //flex: 2
71
+ }, {
72
+ text: '超时回调',
73
+ renderer: function(v, m, record) {
74
+ var names = new Array();
75
+ Ext.Array.forEach(record.get('callbacks'), function(callback, index) {names.push(callback.name)});
76
+ return names.join(', ');
77
+ },
78
+ flex: 3
75
79
  }, {
76
80
  text: '生效时间',
77
81
  renderer: function(v, m, record) {
@@ -0,0 +1,129 @@
1
+ // Note: add the styles shown in the comment at the end of this code block to your .css
2
+ Ext.ns('Ext.ux.form'); // create namespace
3
+ Ext.define('Ext.ux.form.CheckboxListCombo', {
4
+ extend: 'Ext.form.field.ComboBox',
5
+ alias: 'widget.checkboxlistcombo',
6
+
7
+ /**
8
+ * The following options were added to extend the ComboBox to provide additional functionality.
9
+ * The are now 4 possible display values in the text box of the combo:
10
+ * (1) .emptyText
11
+ * (2) .displayField - if only one item is selected
12
+ * (3) optional .briefDisplayField - if 2 or more items are selected
13
+ * (4) optional {nn} briefSummaryTitle - if more than a certain number of items are selected (and all won't fit)
14
+ * These features are only used if the values below are not falsy
15
+ */
16
+ briefDisplayField: false, // store field to use if there are multiple items selected (if not false)
17
+ briefDisplayLimit: false, // max # of "briefDisplayField" items to display, after which "## {briefSummaryTitle}" is displayed
18
+ briefSummaryTitle: false, // string to display if there are too many selected items for the display box
19
+ displayList: "", // will hold the delimited list of all selections (may not be in the combo if too many, but can be used as desired)
20
+ firstItemChecksAll: false, // if true then the first item is ignored other than for this purpose (value must be falsy)
21
+ allSelectedTitle: false, // if not set, it will be set to the displayValue of the first item
22
+
23
+ constructor: function(config) {
24
+ Ext.ux.form.CheckboxListCombo.superclass.constructor.call(this, config);
25
+ },
26
+
27
+ initComponent: function () {
28
+ if (this.briefDisplayField) {
29
+ this.briefDisplayTpl = Ext.create('Ext.XTemplate', '<tpl for=".">{[typeof values === "string" ? values : values.' + this.briefDisplayField + ']}<tpl if="xindex < xcount">' + this.delimiter + '</tpl></tpl>');
30
+ }
31
+ Ext.ux.form.CheckboxListCombo.superclass.initComponent.apply(this, arguments);
32
+ this.listConfig.checkboxComboId = this.id; // for firstItem checking (see below)
33
+ },
34
+
35
+ getDisplayValue: function () {
36
+ this.displayList = this.displayTplData && this.displayTplData.length
37
+ ? (this.briefDisplayTpl || this.displayTpl).apply(this.displayTplData)
38
+ : "";
39
+ var ttl = this.allSelected
40
+ ? this.allSelectedTitle || "[" + this.emptyText + "]"
41
+ : (
42
+ (this.briefDisplayLimit && this.briefSummaryTitle && this.displayTplData && this.displayTplData.length > this.briefDisplayLimit)
43
+ ? (this.displayTplData.length) + " " + this.briefSummaryTitle
44
+ : this.displayList
45
+ );
46
+ return ttl || this.emptyText;
47
+ },
48
+
49
+ lastQuery: '', // prevents clearing of the list after initial setValue
50
+ listConfig: {
51
+ getInnerTpl: function (displayField) {
52
+ return '<tpl for="."><div><img src="' + Ext.BLANK_IMAGE_URL + '" ' + 'class="ux-checkboxlistcombo-icon">{' + (displayField || 'text') + ':htmlEncode}</div></tpl>';
53
+ },
54
+ // from here down it's all about the first item checking/unchecking all others
55
+ previousAllChecked: false,
56
+ previousCheckCount: 0,
57
+ listeners: {
58
+ beforeselect: function ( me, node, selections, options ) {
59
+ // since selectionChange does not provide info on which node changed,
60
+ // we need to determine whether the all item was selected...
61
+ var combo = Ext.getCmp(me.view.checkboxComboId);
62
+ me.view.somethingChecked = true;
63
+ me.view.allChecked = combo && combo.firstItemChecksAll && !node.data[combo.valueField];
64
+ return true;
65
+ },
66
+ selectionchange: function (dataViewModel, selections, options) {
67
+ var me = dataViewModel,combo = Ext.getCmp(me.view.checkboxComboId),
68
+ storeRecs, recs = [], d, i, j, nChecked, vField, dField, allState, allNodes, allItem,
69
+ grayCls = "ux-checkboxcombolist-tri",
70
+ somethingChecked = me.view.somethingChecked,
71
+ allChecked = me.view.allChecked;
72
+ me.view.somethingChecked = false;
73
+ me.view.allChecked = false; // beforeselect doesn't fire on deselect
74
+ if (combo && combo.firstItemChecksAll) {
75
+ allNodes = me.view.getNodes();
76
+ if (allNodes.length) {
77
+ allItem = Ext.get(allNodes[0]);
78
+ vField = combo.valueField;
79
+ dField = combo.displayField;
80
+ storeRecs = Ext.clone(me.store.getRange(0));
81
+ for (i=nChecked=0;i<storeRecs.length;i++) {
82
+ d = storeRecs[i].data;
83
+ d.checked = false;
84
+ for (j=0;selections && !d.checked && j<selections.length;j++) {
85
+ if (selections[j].data[vField] == d[vField]) {
86
+ d.checked = true;
87
+ if (i>0) {
88
+ nChecked++;
89
+ } else if (!combo.allSelectedTitle) {
90
+ combo.allSelectedTitle = d[dField];
91
+ }
92
+ }
93
+ }
94
+ recs.push(d);
95
+ }
96
+ allState = ( ( recs[0].checked && allChecked ) || (nChecked == recs.length-1 && somethingChecked))
97
+ ? 1
98
+ : ( 0 < nChecked && nChecked < recs.length-1 ? 2 : 0);
99
+
100
+ me.view.suspendEvents();// suspend events, though selectAll & deselectAll send them anyway
101
+ me.suspendEvents();
102
+ switch (allState) {
103
+ case 0: // None
104
+ //me.deselectAll(true); // Nope, doesn't suspend events
105
+ combo.allSelected = false;
106
+ allItem.removeCls(grayCls);
107
+ setTimeout(function () { me.deselectAll(false); },1); // suspendEvent is ignored, so we hack using a timer
108
+ break;
109
+ case 1: // All
110
+ //me.selectAll(true); // Nope, doesn't suspend events
111
+ combo.allSelected = true;
112
+ allItem.removeCls(grayCls);
113
+ setTimeout(function () { me.selectAll(false); },1); // suspendEvent is ignored, so we hack using a timer
114
+ break;
115
+ case 2: // Some (gray out the ALL item)
116
+ allItem.addCls(grayCls);
117
+ combo.allSelected = false;
118
+ me.deselect(0,true); // in this case the suspendEvent flag works
119
+ break;
120
+ }
121
+ me.resumeEvents();// resume events, though selectAll & deselectAll send them anyway
122
+ me.view.resumeEvents();
123
+ console.log("CheckboxComboList.changed: " + allState + " / " + nChecked);
124
+ }
125
+ }
126
+ }
127
+ }
128
+ },
129
+ });
@@ -0,0 +1,10 @@
1
+ .ux-checkboxlistcombo-icon {
2
+ float: left; width:16px; height:16px;
3
+ background-position: -1px -1px ! important; background-repeat: no-repeat ! important;
4
+ }
5
+ .x-boundlist-item>div>.ux-checkboxlistcombo-icon {
6
+ background: url(image_path("extjs/resources/themes/images/default/grid/unchecked.gif")) no-repeat;
7
+ }
8
+ .x-boundlist-item.x-boundlist-selected>div>.ux-checkboxlistcombo-icon {
9
+ background: url(image_path("extjs/resources/themes/images/default/grid/checked.gif")) no-repeat;
10
+ }
@@ -9,6 +9,7 @@
9
9
  * compiled file, but it's generally better to create a new file per style scope.
10
10
  *
11
11
  *= require_self
12
+ *= require ./checkboxlistcombo
12
13
  */
13
14
  .btn-add-icon {
14
15
  background: url(image_path('task-manager/icons/add.png')) no-repeat;
@@ -47,12 +47,11 @@ module TaskManager
47
47
  # }, ...]
48
48
  # }
49
49
  def index
50
- plans = TaskManager::Plan.search(params[:q]).result.order('id DESC')
50
+ plans = TaskManager::Plan.page(params[:page]).per(params[:limit]).
51
+ order('id DESC').search(params[:q]).result
51
52
  result = {
52
- total: plans.count,
53
- plans: ActiveModel::ArraySerializer.new(
54
- plans.page(params[:page]).per(params[:limit])
55
- ).as_json
53
+ total: plans.total_count,
54
+ plans: ActiveModel::ArraySerializer.new(plans).as_json
56
55
  }
57
56
 
58
57
  render json: result, status: :ok
@@ -217,6 +216,7 @@ module TaskManager
217
216
  # }
218
217
  def update
219
218
  plan.assignables.destroy_all
219
+ plan.callables.destroy_all
220
220
 
221
221
  if plan.update_attributes(params[:plan])
222
222
  render json: plan, status: :ok
@@ -45,12 +45,11 @@ module TaskManager
45
45
  # }, ...]
46
46
  # }
47
47
  def index
48
- tasks = TaskManager::Task.search(params[:q]).result.order('id DESC')
48
+ tasks = TaskManager::Task.page(params[:page]).per(params[:limit]).
49
+ order('id DESC').search(params[:q]).result
49
50
  result = {
50
- total: tasks.count,
51
- tasks: ActiveModel::ArraySerializer.new(
52
- tasks.page(params[:page]).per(params[:limit])
53
- ).as_json
51
+ total: tasks.total_count,
52
+ tasks: ActiveModel::ArraySerializer.new(tasks).as_json
54
53
  }
55
54
 
56
55
  render json: result, status: :ok
@@ -4,5 +4,6 @@ module TaskManager
4
4
  :begin_to_remind, :autocompletable, :created_at, :updated_at
5
5
 
6
6
  has_many :assignees
7
+ has_many :callbacks
7
8
  end
8
9
  end
@@ -1,3 +1,3 @@
1
1
  module TaskManager
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: task-manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-29 00:00:00.000000000 Z
12
+ date: 2012-12-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -249,10 +249,12 @@ files:
249
249
  - app/assets/javascripts/task-manager/extjs/app/controller/Tasks.js
250
250
  - app/assets/javascripts/task-manager/extjs/app/helper/ApplicationHelper.js
251
251
  - app/assets/javascripts/task-manager/extjs/app/model/Assignee.js
252
+ - app/assets/javascripts/task-manager/extjs/app/model/Callback.js
252
253
  - app/assets/javascripts/task-manager/extjs/app/model/Plan.js
253
254
  - app/assets/javascripts/task-manager/extjs/app/model/Task.js
254
255
  - app/assets/javascripts/task-manager/extjs/app/store/Assignees.js
255
256
  - app/assets/javascripts/task-manager/extjs/app/store/Booleans.js
257
+ - app/assets/javascripts/task-manager/extjs/app/store/Callbacks.js
256
258
  - app/assets/javascripts/task-manager/extjs/app/store/Days.js
257
259
  - app/assets/javascripts/task-manager/extjs/app/store/Hours.js
258
260
  - app/assets/javascripts/task-manager/extjs/app/store/Minutes.js
@@ -264,6 +266,7 @@ files:
264
266
  - app/assets/javascripts/task-manager/extjs/app/store/Types.js
265
267
  - app/assets/javascripts/task-manager/extjs/app/store/WeekDays.js
266
268
  - app/assets/javascripts/task-manager/extjs/app/view/assignee/TreeCombo.js
269
+ - app/assets/javascripts/task-manager/extjs/app/view/callback/CheckboxCombo.js
267
270
  - app/assets/javascripts/task-manager/extjs/app/view/plan/Form.js
268
271
  - app/assets/javascripts/task-manager/extjs/app/view/plan/FormWindow.js
269
272
  - app/assets/javascripts/task-manager/extjs/app/view/plan/Grid.js
@@ -272,11 +275,13 @@ files:
272
275
  - app/assets/javascripts/task-manager/extjs/app/view/task/Grid.js
273
276
  - app/assets/javascripts/task-manager/extjs/app/view/task/Index.js
274
277
  - app/assets/javascripts/task-manager/extjs/app/view/task/Search.js
278
+ - app/assets/javascripts/task-manager/extjs/lib/ux/CheckboxListCombo.js
275
279
  - app/assets/javascripts/task-manager/extjs/lib/ux/Model.js
276
280
  - app/assets/javascripts/task-manager/extjs/lib/ux/MultiSelectablePagingGrid.js
277
281
  - app/assets/javascripts/task-manager/extjs/lib/ux/RowExpander.js
278
282
  - app/assets/javascripts/task-manager/extjs/lib/ux/TreeCombo.js
279
283
  - app/assets/javascripts/task-manager/extjs.js
284
+ - app/assets/stylesheets/task-manager/checkboxlistcombo.css.scss
280
285
  - app/assets/stylesheets/task-manager/extjs.css.scss
281
286
  - app/controllers/task_manager/api/v1/plans_controller.rb
282
287
  - app/controllers/task_manager/api/v1/tasks_controller.rb
@@ -319,7 +324,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
319
324
  version: '0'
320
325
  segments:
321
326
  - 0
322
- hash: -799618091743939836
327
+ hash: 2162932120880694104
323
328
  required_rubygems_version: !ruby/object:Gem::Requirement
324
329
  none: false
325
330
  requirements:
@@ -328,7 +333,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
328
333
  version: '0'
329
334
  segments:
330
335
  - 0
331
- hash: -799618091743939836
336
+ hash: 2162932120880694104
332
337
  requirements: []
333
338
  rubyforge_project:
334
339
  rubygems_version: 1.8.24