task-manager 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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