task-manager 0.1.2 → 0.1.3

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