task-manager 0.1.1

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 (69) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +4 -0
  3. data/Rakefile +27 -0
  4. data/app/assets/images/task-manager/icons/add.png +0 -0
  5. data/app/assets/images/task-manager/icons/delete.png +0 -0
  6. data/app/assets/images/task-manager/icons/edit.png +0 -0
  7. data/app/assets/javascripts/task-manager/application.js +43 -0
  8. data/app/assets/javascripts/task-manager/extjs/app/controller/Plans.js +359 -0
  9. data/app/assets/javascripts/task-manager/extjs/app/controller/Tasks.js +56 -0
  10. data/app/assets/javascripts/task-manager/extjs/app/helper/application_helper.js +24 -0
  11. data/app/assets/javascripts/task-manager/extjs/app/model/Assignee.js +19 -0
  12. data/app/assets/javascripts/task-manager/extjs/app/model/Plan.js +195 -0
  13. data/app/assets/javascripts/task-manager/extjs/app/model/Task.js +23 -0
  14. data/app/assets/javascripts/task-manager/extjs/app/store/Assignees.js +53 -0
  15. data/app/assets/javascripts/task-manager/extjs/app/store/AssigneesTree.js +3 -0
  16. data/app/assets/javascripts/task-manager/extjs/app/store/Booleans.js +9 -0
  17. data/app/assets/javascripts/task-manager/extjs/app/store/Days.js +38 -0
  18. data/app/assets/javascripts/task-manager/extjs/app/store/Hours.js +31 -0
  19. data/app/assets/javascripts/task-manager/extjs/app/store/Minutes.js +67 -0
  20. data/app/assets/javascripts/task-manager/extjs/app/store/Months.js +19 -0
  21. data/app/assets/javascripts/task-manager/extjs/app/store/Plans.js +6 -0
  22. data/app/assets/javascripts/task-manager/extjs/app/store/QuarterlyMonths.js +10 -0
  23. data/app/assets/javascripts/task-manager/extjs/app/store/Statuses.js +11 -0
  24. data/app/assets/javascripts/task-manager/extjs/app/store/Tasks.js +6 -0
  25. data/app/assets/javascripts/task-manager/extjs/app/store/Types.js +12 -0
  26. data/app/assets/javascripts/task-manager/extjs/app/store/WeekDays.js +14 -0
  27. data/app/assets/javascripts/task-manager/extjs/app/view/plan/AssignablesWindow.js +23 -0
  28. data/app/assets/javascripts/task-manager/extjs/app/view/plan/Edit.js +189 -0
  29. data/app/assets/javascripts/task-manager/extjs/app/view/plan/EditWindow.js +21 -0
  30. data/app/assets/javascripts/task-manager/extjs/app/view/plan/Grid.js +92 -0
  31. data/app/assets/javascripts/task-manager/extjs/app/view/plan/Index.js +24 -0
  32. data/app/assets/javascripts/task-manager/extjs/app/view/plan/New.js +190 -0
  33. data/app/assets/javascripts/task-manager/extjs/app/view/plan/Search.js +132 -0
  34. data/app/assets/javascripts/task-manager/extjs/app/view/plan/SelectAssignables.js +22 -0
  35. data/app/assets/javascripts/task-manager/extjs/app/view/plan/SelectAssignablesGrid.js +22 -0
  36. data/app/assets/javascripts/task-manager/extjs/app/view/plan/SelectAssignablesTree.js +9 -0
  37. data/app/assets/javascripts/task-manager/extjs/app/view/plan/Window.js +21 -0
  38. data/app/assets/javascripts/task-manager/extjs/app/view/task/Grid.js +62 -0
  39. data/app/assets/javascripts/task-manager/extjs/app/view/task/Index.js +21 -0
  40. data/app/assets/javascripts/task-manager/extjs/app/view/task/Search.js +148 -0
  41. data/app/assets/javascripts/task-manager/extjs/lib/ux/MultiSelectablePagingGrid.js +20 -0
  42. data/app/assets/javascripts/task-manager/extjs/lib/ux/RowExpander.js +230 -0
  43. data/app/assets/stylesheets/task-manager/application.css.scss +13 -0
  44. data/app/assets/stylesheets/task-manager/extjs.css.scss +43 -0
  45. data/app/controllers/task_manager/api/v1/plans_controller.rb +251 -0
  46. data/app/controllers/task_manager/api/v1/tasks_controller.rb +82 -0
  47. data/app/controllers/task_manager/application_controller.rb +4 -0
  48. data/app/helpers/task_manager/application_helper.rb +4 -0
  49. data/app/models/task_manager/assignable.rb +10 -0
  50. data/app/models/task_manager/callable.rb +10 -0
  51. data/app/models/task_manager/plan.rb +114 -0
  52. data/app/models/task_manager/task.rb +81 -0
  53. data/app/serializers/task_manager/plan_serializer.rb +8 -0
  54. data/app/serializers/task_manager/task_serializer.rb +8 -0
  55. data/app/views/layouts/task-manager/application.html.erb +14 -0
  56. data/config/routes.rb +8 -0
  57. data/db/migrate/20121102054723_setup_hstore.rb +9 -0
  58. data/db/migrate/20121102055137_create_task_manager_plans.rb +16 -0
  59. data/db/migrate/20121102142657_create_task_manager_assignables.rb +13 -0
  60. data/db/migrate/20121102150720_create_task_manager_callables.rb +13 -0
  61. data/db/migrate/20121105170602_create_task_manager_tasks.rb +18 -0
  62. data/lib/task-manager.rb +37 -0
  63. data/lib/task-manager/api_constraints.rb +12 -0
  64. data/lib/task-manager/deadline_calculator.rb +22 -0
  65. data/lib/task-manager/deadline_validator.rb +27 -0
  66. data/lib/task-manager/engine.rb +5 -0
  67. data/lib/task-manager/version.rb +3 -0
  68. data/lib/tasks/task-manager_tasks.rake +4 -0
  69. metadata +327 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ TaskManager
2
+ -----------
3
+
4
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
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
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
@@ -0,0 +1,43 @@
1
+ //
2
+ //= require_tree ./extjs/lib
3
+ //
4
+ //= require_tree ./extjs/app/model
5
+ //= require_tree ./extjs/app/store
6
+ //
7
+ //= require_tree ./extjs/app/helper
8
+ //
9
+ //= require_self
10
+ //
11
+ //= require ./extjs/app/view/task/Search
12
+ //= require ./extjs/app/view/task/Grid
13
+ //= 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
22
+ //= require ./extjs/app/view/plan/Search
23
+ //= require ./extjs/app/view/plan/Grid
24
+ //= require ./extjs/app/view/plan/Index
25
+ //
26
+ //= require_tree ./extjs/app/controller
27
+
28
+ // Register all stores
29
+ Ext.data.StoreManager.register(
30
+ Ext.create('TM.store.Tasks', { storeId: 'TM.store.Tasks'}),
31
+ Ext.create('TM.store.Assignees', { storeId: 'TM.store.Assignees'}),
32
+ Ext.create('TM.store.AssigneesTree', { storeId: 'TM.store.AssigneesTree'}),
33
+ Ext.create('TM.store.WeekDays', { storeId: 'TM.store.WeekDays'}),
34
+ Ext.create('TM.store.QuarterlyMonths', { storeId: 'TM.store.QuarterlyMonths'}),
35
+ Ext.create('TM.store.Months', { storeId: 'TM.store.Months'}),
36
+ Ext.create('TM.store.Minutes', { storeId: 'TM.store.Minutes'}),
37
+ Ext.create('TM.store.Days', { storeId: 'TM.store.Days'}),
38
+ Ext.create('TM.store.Hours', { storeId: 'TM.store.Hours'}),
39
+ Ext.create('TM.store.Booleans', { storeId: 'TM.store.Booleans'}),
40
+ Ext.create('TM.store.Statuses', { storeId: 'TM.store.Statuses'}),
41
+ Ext.create('TM.store.Types', { storeId: 'TM.store.Types'}),
42
+ Ext.create('TM.store.Plans', { storeId: 'TM.store.Plans'})
43
+ );
@@ -0,0 +1,359 @@
1
+ Ext.define('TM.controller.Plans', {
2
+ extend: 'Ext.app.Controller',
3
+
4
+ views: [
5
+ 'plan.Index',
6
+ 'plan.AssignablesWindow'
7
+
8
+ ],
9
+
10
+ models: [
11
+ 'Plan',
12
+ 'Assignee'
13
+ ],
14
+
15
+ refs: [{
16
+ ref: 'searchForm',
17
+ selector: 'plan_search'
18
+ }, {
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"]'
36
+ }, {
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'
48
+ }],
49
+
50
+ init: function() {
51
+ this.control({
52
+ 'plan_search button[action="query"]': {
53
+ click: this.onQueryClick
54
+ },
55
+ 'plan_search button[action="reset"]': {
56
+ click: this.onSearchResetClick
57
+ },
58
+ 'plan_grid button[action="add"]': {
59
+ click: this.onAddClick
60
+ },
61
+ 'plan_grid button[action="edit"]': {
62
+ click: this.onEditClick
63
+ },
64
+ 'plan_grid button[action="delete"]': {
65
+ click: this.onDeleteClick
66
+ },
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"]': {
83
+ click: this.onSaveClick
84
+ },
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
105
+ }
106
+ });
107
+ },
108
+
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) {
196
+ },
197
+
198
+ onEditClick: function(btn) {
199
+ var length = btn.up('plan_grid').getSelectionModel().getSelection().length;
200
+ if (length == 0) {
201
+ Ext.Msg.alert('提示', '请选择要修改的数据');
202
+ return;
203
+ }
204
+ if(length > 1){
205
+ Ext.Msg.alert('提示', '修改的数据一次只能选择一条!');
206
+ return;
207
+ }
208
+
209
+ var record = btn.up('plan_grid').getSelectionModel().getSelection()[0];
210
+
211
+ var win = Ext.create('TM.view.plan.EditWindow');
212
+ win.show();
213
+
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
+ }
253
+ },
254
+
255
+ onDeleteClick: function(btn) {
256
+ var select = btn.up('plan_grid').getSelectionModel().getSelection()[0];
257
+ if(select == null) {
258
+ Ext.Msg.alert('提示','请选择要删除的计划任务');
259
+ return;
260
+ }
261
+
262
+ Ext.Msg.confirm('提示','您确认要删除选中的计划任务吗?', function(b){
263
+ if(b != 'yes') return;
264
+
265
+ var selected = btn.up('plan_grid').getSelectionModel().getSelection();
266
+ Ext.each(selected, function(s){
267
+ s.destroy();
268
+ });
269
+ });
270
+ },
271
+
272
+ onResetClick: function(btn) {
273
+ this.getPlanNew().getForm().reset();
274
+ },
275
+
276
+ onSaveClick: function(btn) {
277
+ 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;
286
+
287
+ var Plan = TM.model.Plan;
288
+ var plan = Plan.create(attrs, {
289
+ success: function() {
290
+ Ext.Msg.alert('提示', '计划添加成功!');
291
+ Ext.getStore('TM.store.Plans').insert(0, plan);
292
+ self.getPlanWindow().close();
293
+ },
294
+ failure: function() {
295
+ Ext.Msg.alert('提示', '计划添加失败!')
296
+ }
297
+ })
298
+ },
299
+
300
+ onQueryClick: function(btn) {
301
+ var params = this.getSearchForm().getValues();
302
+ Ext.getStore('TM.store.Plans').load({ params: params });
303
+ },
304
+
305
+ onSearchResetClick: function(btn) {
306
+ this.getSearchForm().getForm().reset();
307
+ },
308
+
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
+ index: function() {
357
+ this.render('TM.view.plan.Index');
358
+ }
359
+ });