task-manager 0.1.2 → 0.1.3

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.
Files changed (28) hide show
  1. data/README.md +136 -1
  2. data/Rakefile +3 -17
  3. data/app/assets/javascripts/task-manager/{application.js → extjs.js} +3 -9
  4. data/app/assets/javascripts/task-manager/extjs/app/controller/Plans.js +29 -264
  5. data/app/assets/javascripts/task-manager/extjs/app/helper/{application_helper.js → ApplicationHelper.js} +0 -0
  6. data/app/assets/javascripts/task-manager/extjs/app/model/Assignee.js +16 -0
  7. data/app/assets/javascripts/task-manager/extjs/app/model/Plan.js +18 -156
  8. data/app/assets/javascripts/task-manager/extjs/app/store/Assignees.js +37 -30
  9. data/app/assets/javascripts/task-manager/extjs/app/view/assignee/TreeCombo.js +48 -0
  10. data/app/assets/javascripts/task-manager/extjs/app/view/plan/Form.js +290 -0
  11. data/app/assets/javascripts/task-manager/extjs/app/view/plan/{Window.js → FormWindow.js} +7 -5
  12. data/app/assets/javascripts/task-manager/extjs/lib/ux/Model.js +14 -0
  13. data/app/assets/javascripts/task-manager/extjs/lib/ux/TreeCombo.js +276 -0
  14. data/app/controllers/task_manager/api/v1/plans_controller.rb +3 -3
  15. data/app/controllers/task_manager/api/v1/tasks_controller.rb +1 -1
  16. data/app/models/task_manager/plan.rb +48 -37
  17. data/lib/task-manager/deadline_validator.rb +23 -14
  18. data/lib/task-manager/version.rb +1 -1
  19. metadata +27 -16
  20. data/app/assets/javascripts/task-manager/extjs/app/store/AssigneesTree.js +0 -3
  21. data/app/assets/javascripts/task-manager/extjs/app/view/plan/AssignablesWindow.js +0 -23
  22. data/app/assets/javascripts/task-manager/extjs/app/view/plan/Edit.js +0 -189
  23. data/app/assets/javascripts/task-manager/extjs/app/view/plan/EditWindow.js +0 -21
  24. data/app/assets/javascripts/task-manager/extjs/app/view/plan/New.js +0 -190
  25. data/app/assets/javascripts/task-manager/extjs/app/view/plan/SelectAssignables.js +0 -22
  26. data/app/assets/javascripts/task-manager/extjs/app/view/plan/SelectAssignablesGrid.js +0 -22
  27. data/app/assets/javascripts/task-manager/extjs/app/view/plan/SelectAssignablesTree.js +0 -9
  28. data/app/assets/stylesheets/task-manager/application.css.scss +0 -13
data/README.md CHANGED
@@ -1,4 +1,139 @@
1
1
  TaskManager
2
2
  -----------
3
3
 
4
- This project rocks and uses MIT-LICENSE.
4
+ [![Build Status](https://secure.travis-ci.org/menglifang/task-manager.png?branch=develop)](http://travis-ci.org/menglifang/task-manager)
5
+ [![Dependency Status](https://gemnasium.com/menglifang/task-manager.png)](https://gemnasium.com/menglifang/task-manager)
6
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/menglifang/task-manager)
7
+
8
+ TaskManager是一个可挂载的Rails引擎(Mountable Rails
9
+ Engine),因此您可以在Rails应用中通过挂载的方式来使用。另外,TaskManager使用了PostgreSQL的hstore特性,因此TaskManager只能工作在PostgreSQL数据库之上。
10
+
11
+ 屁话:鉴于PostgreSQL的宽松的免费使用政策,以及其越来越出色的性能,强烈建议您使用之。
12
+
13
+ ### 依赖
14
+
15
+ * PostgreSQL数据库
16
+
17
+ ```bash
18
+ # Ubuntu
19
+ sudo apt-get install postgresql-contrib
20
+ ```
21
+
22
+ ### 安装
23
+
24
+ * 安装Gem包
25
+
26
+ 在您的Rails应用程序的Gemfile文件中添加:
27
+
28
+ ```ruby
29
+ gem 'task-manager', '~>0.1.3'
30
+ ```
31
+
32
+ 然后,运行`bundle install`。
33
+
34
+ * 创建数据库表
35
+
36
+ * 生成数据库迁移文件
37
+
38
+ ```bash
39
+ rake task_manager:install:migrations
40
+ ```
41
+
42
+ * 创建数据库表
43
+
44
+ ```bash
45
+ rake db:migrate
46
+ ```
47
+
48
+ ### 使用说明
49
+
50
+ #### 挂载TaskManager API
51
+
52
+ 编辑`config/routes.rb`文件,在其中挂载TaskManager路由。
53
+
54
+ ```ruby
55
+ mount TaskManager::Engine => "/task-manager"
56
+ ```
57
+
58
+ 使用`rake
59
+ routes`命令,检查是否正常挂载成功。如果挂载成功,命令行中将会有如下输出:
60
+
61
+ ```
62
+ Routes for TaskManager::Engine:
63
+ api_plans GET /api/plans(.:format) task_manager/api/v1/plans#index {:format=>"json"}
64
+ POST /api/plans(.:format) task_manager/api/v1/plans#create {:format=>"json"}
65
+ api_plan PUT /api/plans/:id(.:format) task_manager/api/v1/plans#update {:format=>"json"}
66
+ DELETE /api/plans/:id(.:format) task_manager/api/v1/plans#destroy {:format=>"json"}
67
+ api_tasks GET /api/tasks(.:format) task_manager/api/v1/tasks#index {:format=>"json"}
68
+ api_task DELETE /api/tasks/:id(.:format) task_manager/api/v1/tasks#destroy {:format=>"json"}
69
+ ```
70
+
71
+ 详细的接口使用说明请查看[TaskManager
72
+ API文档](http://rdoc.info/github/menglifang/task-manager/master/TaskManager/Api/V1)
73
+
74
+ #### 界面
75
+
76
+ ##### ExtJS
77
+
78
+ * 导入资源文件
79
+
80
+ 在`app/assets/javascripts/application.js`文件中添加:
81
+
82
+ ```javascript
83
+ // 代码需要放置在导入ExtJS库之后
84
+ //= require task-manager/extjs
85
+ ```
86
+
87
+ 在`app/assets/stylesheets/application.css`文件中添加:
88
+
89
+ ```css
90
+ /*= require task-manager/extjs */
91
+ ```
92
+
93
+ ### 开发指南
94
+
95
+ * 安装依赖包
96
+
97
+ ```bash
98
+ bundle install
99
+ ```
100
+
101
+ * 创建数据库
102
+
103
+ ```bash
104
+ cd path/to/task-manager/spec/dummy
105
+
106
+ # 说明:需要根据您安装的数据库设置,修改path/to/task-manager/spec/dummy/config/database.yml中的相应配置
107
+
108
+ # 创建开发数据库
109
+ rake db:create db:migrate db:seed RAILS_ENV=development
110
+
111
+ # 创建测试数据库
112
+ rake db:create db:migrate db:seed RAILS_ENV=test
113
+ ```
114
+
115
+ * 运行后端测试(Ruby)
116
+
117
+ ```bash
118
+ cd path/to/task-manager
119
+ rake
120
+ ```
121
+
122
+ * 运行前端测试(Javascript)
123
+
124
+ ```bash
125
+ cd path/to/task-manager/spec/dummy
126
+ rails s
127
+ ```
128
+
129
+ 然后,打开浏览器访问:[http://localhost:3000/siesta](http://localhost:3000/siesta)。
130
+ 待页面打开后,点击打开页面的运行按钮进行前端测试。
131
+
132
+ * 查看Demo应用
133
+
134
+ ```bash
135
+ cd path/to/task-manager/spec/dummy
136
+ rails s
137
+ ```
138
+
139
+ 然后,打开浏览器访问:[http://localhost:3000/extjs](http://localhost:3000/extjs)。
data/Rakefile CHANGED
@@ -4,24 +4,10 @@ begin
4
4
  rescue LoadError
5
5
  puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
6
  end
7
- begin
8
- require 'rdoc/task'
9
- rescue LoadError
10
- require 'rdoc/rdoc'
11
- require 'rake/rdoctask'
12
- RDoc::Task = Rake::RDocTask
13
- end
14
-
15
- RDoc::Task.new(:rdoc) do |rdoc|
16
- rdoc.rdoc_dir = 'rdoc'
17
- rdoc.title = 'TaskManager'
18
- rdoc.options << '--line-numbers'
19
- rdoc.rdoc_files.include('README.rdoc')
20
- rdoc.rdoc_files.include('lib/**/*.rb')
21
- end
22
-
23
-
24
7
 
8
+ require 'rspec/core/rake_task'
9
+ RSpec::Core::RakeTask.new(:spec)
10
+ task :default => :spec
25
11
 
26
12
  Bundler::GemHelper.install_tasks
27
13
 
@@ -8,17 +8,12 @@
8
8
  //
9
9
  //= require_self
10
10
  //
11
+ //= require ./extjs/app/view/assignee/TreeCombo
11
12
  //= require ./extjs/app/view/task/Search
12
13
  //= require ./extjs/app/view/task/Grid
13
14
  //= require ./extjs/app/view/task/Index
14
- //= require ./extjs/app/view/plan/SelectAssignablesTree
15
- //= require ./extjs/app/view/plan/SelectAssignablesGrid
16
- //= require ./extjs/app/view/plan/SelectAssignables
17
- //= require ./extjs/app/view/plan/AssignablesWindow
18
- //= require ./extjs/app/view/plan/Edit
19
- //= require ./extjs/app/view/plan/EditWindow
20
- //= require ./extjs/app/view/plan/New
21
- //= require ./extjs/app/view/plan/Window
15
+ //= require ./extjs/app/view/plan/Form
16
+ //= require ./extjs/app/view/plan/FormWindow
22
17
  //= require ./extjs/app/view/plan/Search
23
18
  //= require ./extjs/app/view/plan/Grid
24
19
  //= require ./extjs/app/view/plan/Index
@@ -29,7 +24,6 @@
29
24
  Ext.data.StoreManager.register(
30
25
  Ext.create('TM.store.Tasks', { storeId: 'TM.store.Tasks'}),
31
26
  Ext.create('TM.store.Assignees', { storeId: 'TM.store.Assignees'}),
32
- Ext.create('TM.store.AssigneesTree', { storeId: 'TM.store.AssigneesTree'}),
33
27
  Ext.create('TM.store.WeekDays', { storeId: 'TM.store.WeekDays'}),
34
28
  Ext.create('TM.store.QuarterlyMonths', { storeId: 'TM.store.QuarterlyMonths'}),
35
29
  Ext.create('TM.store.Months', { storeId: 'TM.store.Months'}),
@@ -2,9 +2,7 @@ Ext.define('TM.controller.Plans', {
2
2
  extend: 'Ext.app.Controller',
3
3
 
4
4
  views: [
5
- 'plan.Index',
6
- 'plan.AssignablesWindow'
7
-
5
+ 'plan.Index'
8
6
  ],
9
7
 
10
8
  models: [
@@ -16,35 +14,11 @@ Ext.define('TM.controller.Plans', {
16
14
  ref: 'searchForm',
17
15
  selector: 'plan_search'
18
16
  }, {
19
- ref: 'planWindow',
20
- selector: 'plan_window'
21
- }, {
22
- ref: 'planNew',
23
- selector: 'plan_new'
24
- }, {
25
- ref: 'planEditWindow',
26
- selector: 'plan_editwindow'
27
- }, {
28
- ref: 'planEdit',
29
- selector: 'plan_edit'
30
- }, {
31
- ref: 'planType',
32
- selector: 'plan_new combo[name="plan_type"]'
33
- }, {
34
- ref: 'assignablesField',
35
- selector: 'plan_new textfield[id="assignables"]'
17
+ ref: 'planFormWindow',
18
+ selector: 'plan_formwindow'
36
19
  }, {
37
- ref: 'selectAssignablesTree',
38
- selector: 'plan_selectassignablestree'
39
- }, {
40
- ref: 'editAssignablesField',
41
- selector: 'plan_edit textfield[id="editassignables"]'
42
- }, {
43
- ref: 'selectAssignables',
44
- selector: 'plan_selectassignables'
45
- }, {
46
- ref: 'assignablesWindow',
47
- selector: 'plan_assignableswindow'
20
+ ref: 'planForm',
21
+ selector: 'plan_form'
48
22
  }],
49
23
 
50
24
  init: function() {
@@ -64,135 +38,17 @@ Ext.define('TM.controller.Plans', {
64
38
  'plan_grid button[action="delete"]': {
65
39
  click: this.onDeleteClick
66
40
  },
67
- 'plan_new textfield[id="assignables"]': {
68
- render: this.onSelectAssignablesRender
69
- },
70
- 'plan_edit textfield[id="editassignables"]': {
71
- render: this.onEditSelectAssignablesRender
72
- },
73
- 'plan_new': {
74
- afterrender: this.onNewFormAfterRender
75
- },
76
- 'plan_new combo': {
77
- change: this.onPlanTypeChange
78
- },
79
- 'plan_edit combo': {
80
- change: this.onEditPlanTypeChange
81
- },
82
- 'plan_new button[action="save"]': {
41
+ 'plan_form button[action="save"]': {
83
42
  click: this.onSaveClick
84
43
  },
85
- 'plan_new button[action="reset"]': {
86
- click: this.onResetClick
87
- },
88
- 'plan_selectassignables button[action="save"]': {
89
- click: this.onSelectAssignablesSave
90
- },
91
- 'plan_selectassignables button[action="cancel"]': {
92
- click: this.onSelectAssignablesCancel
93
- },
94
- 'plan_edit': {
95
- render: this.onEditFormRender
96
- },
97
- 'plan_edit button[action="update"]': {
98
- click: this.onUpdateClick
99
- },
100
- 'plan_edit button[action="close"]': {
101
- click: this.onCloseClick
102
- },
103
- 'plan_selectassignablestree': {
104
- checkchange: this.onAssigneesTreeCheckChange
44
+ 'plan_form button[action="cancel"]': {
45
+ click: this.onCancelClick
105
46
  }
106
47
  });
107
48
  },
108
49
 
109
- onAssigneesTreeCheckChange: function(node, checked, opts) {
110
- node.set('checked', checked);
111
- node.cascadeBy(function(n) {
112
- n.set('checked', checked)
113
- });
114
- },
115
-
116
- onSelectAssignablesRender: function() {
117
- Ext.getStore('TM.store.Assignees').reload();
118
- Ext.getStore('TM.store.AssigneesTree')
119
- .setRootNode(Ext.getStore('TM.store.Assignees').toTreeStore().root);
120
-
121
- this.getAssignablesField().getEl().on('click', this.onSelectAssignables, this, {action: 'create'});
122
- },
123
-
124
- onEditSelectAssignablesRender: function() {
125
- this.getEditAssignablesField().getEl().on('click', this.onSelectAssignables, this, {action: 'edit'});
126
- },
127
-
128
- onSelectAssignables: function(eventName, fn, opts) {
129
- Ext.create('TM.view.plan.AssignablesWindow', { 'action': opts.action }).show();
130
- },
131
-
132
- onSelectAssignablesCancel: function(btn) {
133
- btn.up('plan_assignableswindow').close();
134
- },
135
-
136
- onSelectAssignablesSave: function(btn) {
137
- var nodes = Ext.getStore('TM.store.AssigneesTree').getRootNode().childNodes;
138
- var action = btn.up('plan_assignableswindow').action;
139
- var view = action == 'create' ? this.getPlanNew() : this.getPlanEdit();
140
- var assigneesField = action == 'create' ? this.getAssignablesField() : this.getEditAssignablesField()
141
-
142
- view.assignees = [];
143
- var assignees = view.assignees;
144
-
145
- var results = [];
146
- this.generateResult(assignees, results, nodes);
147
-
148
- assigneesField.setValue(results.join(', '));
149
- this.getAssignablesWindow().close();
150
- },
151
-
152
- generateResult: function(assignees, results, nodes) {
153
- var self = this;
154
- Ext.Array.forEach(nodes, function(node, index, nodes) {
155
- if (node.get('leaf')) {
156
- if (node.get('checked')) {
157
- results.push(node.get('text'));
158
- assignees.push(node);
159
- }
160
- } else {
161
- self.generateResult(assignees, results, node.childNodes);
162
- }
163
- });
164
- },
165
-
166
- onUpdateClick: function(btn) {
167
- var self = this;
168
- var attrs = this.getPlanEdit().getValues();
169
-
170
- attrs.assignees = this.getPlanEdit().assignees;
171
-
172
- // attrs.enabled_at = new Date(document.getElementById("enabled_at").value);
173
- var date = this.getPlanEdit().getValues().enabled_at;
174
- attrs.enabled_at = Ext.Date.parse(date, "Y/m/d", true);
175
-
176
- attrs.autocompletable = (attrs.autocompletable == 'on') ? true : false;
177
-
178
-
179
- var record = btn.up('plan_edit').getRecord();
180
- record.update(attrs, {
181
- success: function(record, operation) {
182
- Ext.Msg.alert('提示', '计划更新成功!');
183
- self.getPlanEditWindow().close();
184
- },
185
- failure: function() {
186
- Ext.Msg.alert('提示', '计划更新失败!')
187
- }
188
- })
189
- },
190
-
191
- onCloseClick: function(btn) {
192
- btn.up('plan_editwindow').close();
193
- },
194
-
195
- onEditFormRender: function(record) {
50
+ onAddClick: function() {
51
+ Ext.create('TM.view.plan.FormWindow', { title: '添加计划' }).show();
196
52
  },
197
53
 
198
54
  onEditClick: function(btn) {
@@ -201,55 +57,17 @@ Ext.define('TM.controller.Plans', {
201
57
  Ext.Msg.alert('提示', '请选择要修改的数据');
202
58
  return;
203
59
  }
204
- if(length > 1){
205
- Ext.Msg.alert('提示', '修改的数据一次只能选择一条!');
206
- return;
207
- }
60
+ if(length > 1){
61
+ Ext.Msg.alert('提示', '修改的数据一次只能选择一条!');
62
+ return;
63
+ }
208
64
 
209
65
  var record = btn.up('plan_grid').getSelectionModel().getSelection()[0];
210
66
 
211
- var win = Ext.create('TM.view.plan.EditWindow');
67
+ var win = Ext.create('TM.view.plan.FormWindow', { title: '修改计划' } );
212
68
  win.show();
213
69
 
214
- win.down('plan_edit').loadRecord(record);
215
-
216
- this.generateEditTree(record.get('assignees'));
217
-
218
- var results = [];
219
- this.getPlanEdit().assignees = [];
220
- var assignees = this.getPlanEdit().assignees;
221
- var nodes = Ext.getStore('TM.store.AssigneesTree').getRootNode().childNodes;
222
- this.generateResult(assignees, results, nodes);
223
- this.getEditAssignablesField().setValue(results.join(', '));
224
- },
225
-
226
- generateEditTree: function(assignees) {
227
- var self = this;
228
- Ext.getStore('TM.store.Assignees').reload();
229
- Ext.getStore('TM.store.AssigneesTree').
230
- setRootNode(Ext.getStore('TM.store.Assignees').toTreeStore().root);
231
-
232
- Ext.Array.each(Ext.getStore('TM.store.AssigneesTree').tree.root.childNodes,
233
- function(node, index, nodes) {
234
- self.checkNodes(node, assignees);
235
- });
236
-
237
- },
238
-
239
- checkNodes: function(node, assignees) {
240
- var self = this;
241
- if (node.get('leaf')) {
242
- Ext.Array.each(assignees, function(assignee, index, assignees) {
243
- if (assignee.id == node.get('id')) {
244
- self.getPlanEdit().assignees.push(node);
245
- node.set('checked', true);
246
- }
247
- });
248
- } else {
249
- Ext.Array.each(node.childNodes, function(n, index, nodes) {
250
- self.checkNodes(n, assignees)
251
- });
252
- }
70
+ this.getPlanForm().loadRecord(record);
253
71
  },
254
72
 
255
73
  onDeleteClick: function(btn) {
@@ -269,32 +87,26 @@ Ext.define('TM.controller.Plans', {
269
87
  });
270
88
  },
271
89
 
272
- onResetClick: function(btn) {
273
- this.getPlanNew().getForm().reset();
274
- },
275
-
276
90
  onSaveClick: function(btn) {
277
91
  var self = this;
278
- var attrs = this.getPlanNew().getValues();
279
-
280
- attrs.assignees = this.getPlanNew().assignees;
281
-
282
- var date = this.getPlanNew().getValues().enabled_at;
283
- attrs.enabled_at = Ext.Date.parse(date, "Y/m/d", true);
284
-
285
- attrs.autocompletable = (attrs.autocompletable == 'on') ? true : false;
92
+ var attrs = this.getPlanForm().getValues();
93
+ var record = this.getPlanForm().getRecord() ||
94
+ Ext.create('TM.model.Plan');
286
95
 
287
- var Plan = TM.model.Plan;
288
- var plan = Plan.create(attrs, {
96
+ record.set(attrs);
97
+ record.save({
289
98
  success: function() {
290
- Ext.Msg.alert('提示', '计划添加成功!');
291
- Ext.getStore('TM.store.Plans').insert(0, plan);
292
- self.getPlanWindow().close();
99
+ Ext.Msg.alert('提示', '保存计划成功!');
100
+ self.getPlanFormWindow().close();
293
101
  },
294
102
  failure: function() {
295
- Ext.Msg.alert('提示', '计划添加失败!')
103
+ Ext.Msg.alert('提示', '保存计划失败!')
296
104
  }
297
- })
105
+ });
106
+ },
107
+
108
+ onCancelClick: function(btn) {
109
+ this.getPlanFormWindow().close();
298
110
  },
299
111
 
300
112
  onQueryClick: function(btn) {
@@ -306,53 +118,6 @@ Ext.define('TM.controller.Plans', {
306
118
  this.getSearchForm().getForm().reset();
307
119
  },
308
120
 
309
- onAddClick: function() {
310
- Ext.create('TM.view.plan.Window').show();
311
- },
312
-
313
- onNewFormAfterRender: function() {
314
- this.getPlanType().setValue('daily');
315
- },
316
-
317
- onPlanTypeChange: function(combo, value, oldValue) {
318
- if (value == oldValue) return;
319
-
320
- if(value == 'daily') {
321
- this.getPlanNew().getComponent('fillField').getComponent('new_begin_to_remind').setDisabled(true);
322
- this.getPlanNew().showDailyField();
323
- } else {
324
- this.getPlanNew().getComponent('fillField').getComponent('new_begin_to_remind').setDisabled(false);
325
- if(value == 'yearly') {
326
- this.getPlanNew().showYearlyField();
327
- } else if(value == 'quarterly') {
328
- this.getPlanNew().showQuarterlyField();
329
- } else if(value == 'monthly') {
330
- this.getPlanNew().showMonthlyField();
331
- } else if(value == 'weekly') {
332
- this.getPlanNew().showWeeklyField();
333
- }
334
- }
335
- },
336
-
337
- onEditPlanTypeChange: function(combo, value, oldValue) {
338
- if (value == oldValue) return;
339
-
340
- if(value == 'daily') {
341
- this.getPlanEdit().getComponent('editFillField').getComponent('edit_begin_to_remind').setDisabled(true);
342
- this.getPlanEdit().showDailyField();
343
- } else {
344
- this.getPlanEdit().getComponent('editFillField').getComponent('edit_begin_to_remind').setDisabled(false);
345
- if(value == 'yearly') {
346
- this.getPlanEdit().showYearlyField();
347
- } else if(value == 'quarterly') {
348
- this.getPlanEdit().showQuarterlyField();
349
- } else if(value == 'monthly') {
350
- this.getPlanEdit().showMonthlyField();
351
- } else if(value == 'weekly') {
352
- this.getPlanEdit().showWeeklyField();
353
- }
354
- }
355
- },
356
121
  index: function() {
357
122
  this.render('TM.view.plan.Index');
358
123
  }