task-manager 0.1.1

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