clockface 1.0.0.beta

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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +210 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/config/clockface_manifest.js +2 -0
  6. data/app/assets/images/clockface/clockface.svg +34 -0
  7. data/app/assets/javascripts/clockface/application.js +17 -0
  8. data/app/assets/javascripts/clockface/flash.js +7 -0
  9. data/app/assets/javascripts/clockface/sorttable.js +494 -0
  10. data/app/assets/stylesheets/clockface/application.scss +80 -0
  11. data/app/assets/stylesheets/clockface/application/_fonts.scss +2 -0
  12. data/app/assets/stylesheets/clockface/application/colors.scss +8 -0
  13. data/app/assets/stylesheets/clockface/application/flash.scss +6 -0
  14. data/app/assets/stylesheets/clockface/application/footer.scss +37 -0
  15. data/app/assets/stylesheets/clockface/application/nav.scss +51 -0
  16. data/app/assets/stylesheets/clockface/events/delete.scss +45 -0
  17. data/app/assets/stylesheets/clockface/events/event_form.scss +62 -0
  18. data/app/assets/stylesheets/clockface/events/index.scss +56 -0
  19. data/app/assets/stylesheets/clockface/tasks/delete.scss +29 -0
  20. data/app/assets/stylesheets/clockface/tasks/index.scss +47 -0
  21. data/app/assets/stylesheets/clockface/tasks/task_form.scss +20 -0
  22. data/app/controllers/clockface/application_controller.rb +20 -0
  23. data/app/controllers/clockface/events_controller.rb +151 -0
  24. data/app/controllers/clockface/root_controller.rb +7 -0
  25. data/app/controllers/clockface/tasks_controller.rb +137 -0
  26. data/app/events/clockface/application_job.rb +4 -0
  27. data/app/helpers/clockface/application_helper.rb +4 -0
  28. data/app/helpers/clockface/config_helper.rb +32 -0
  29. data/app/helpers/clockface/events_helper.rb +37 -0
  30. data/app/helpers/clockface/logging_helper.rb +12 -0
  31. data/app/mailers/clockface/application_mailer.rb +6 -0
  32. data/app/models/clockface/application_record.rb +7 -0
  33. data/app/models/clockface/event.rb +179 -0
  34. data/app/models/clockface/task.rb +12 -0
  35. data/app/presenters/clockface/events_presenter.rb +48 -0
  36. data/app/services/clockface/event_validation_interactor.rb +35 -0
  37. data/app/services/clockface/task_validation_interactor.rb +25 -0
  38. data/app/views/clockface/application/_flash.html.erb +25 -0
  39. data/app/views/clockface/application/_footer.html.erb +15 -0
  40. data/app/views/clockface/application/_nav.html.erb +19 -0
  41. data/app/views/clockface/events/_event_form.html.erb +130 -0
  42. data/app/views/clockface/events/delete.html.erb +124 -0
  43. data/app/views/clockface/events/edit.html.erb +14 -0
  44. data/app/views/clockface/events/index.html.erb +108 -0
  45. data/app/views/clockface/events/new.html.erb +14 -0
  46. data/app/views/clockface/tasks/_task_form.html.erb +57 -0
  47. data/app/views/clockface/tasks/delete.html.erb +83 -0
  48. data/app/views/clockface/tasks/edit.html.erb +14 -0
  49. data/app/views/clockface/tasks/index.html.erb +70 -0
  50. data/app/views/clockface/tasks/new.html.erb +14 -0
  51. data/app/views/layouts/clockface/application.html.erb +27 -0
  52. data/config/locales/en.yml +158 -0
  53. data/config/routes.rb +15 -0
  54. data/db/migrate/20170528230549_create_clockface_tasks.rb +10 -0
  55. data/db/migrate/20170528234810_create_clockface_events.rb +20 -0
  56. data/lib/clockface.rb +135 -0
  57. data/lib/clockface/engine.rb +79 -0
  58. data/lib/clockface/version.rb +3 -0
  59. data/lib/clockwork/database_events/synchronizer.rb +73 -0
  60. data/lib/tasks/clockface_tasks.rake +4 -0
  61. metadata +199 -0
@@ -0,0 +1,108 @@
1
+ <div class="container">
2
+ <%= render "clockface/application/flash" %>
3
+
4
+ <div class="events-index__heading-banner">
5
+ <h1>
6
+ <%= t(".heading").downcase %>
7
+ </h1>
8
+
9
+ <%= link_to clockface.new_event_path, class: "events-index__new-link" do %>
10
+ <button type="button" class="events-index__new-btn btn btn-success">
11
+ <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
12
+ </button>
13
+ <% end %>
14
+ </div>
15
+
16
+ <table class="events-index__events table table-striped sortable">
17
+ <thead class="thead-inverse">
18
+ <tr class="events-index__events-header-row">
19
+ <th class="events-index__events-column events-index__events-column--id">
20
+ <%= Clockface::Event.human_attribute_name("id") %>
21
+ </th>
22
+
23
+ <th class="events-index__events-column events-index__events-column--name">
24
+ <%= Clockface::Event.human_attribute_name("name") %>
25
+ </th>
26
+
27
+ <th class="events-index__events-column events-index__events-column--period">
28
+ <%= Clockface::Event.human_attribute_name("period") %>
29
+ </th>
30
+
31
+ <th class="events-index__events-column events-index__events-column--at">
32
+ <%= Clockface::Event.human_attribute_name("at") %>
33
+ </th>
34
+
35
+ <th class="events-index__events-column events-index__events-column--time_zone">
36
+ <%= Clockface::Event.human_attribute_name("time_zone") %>
37
+ </th>
38
+
39
+ <th class="events-index__events-column events-index__events-column--if_condition">
40
+ <%= Clockface::Event.human_attribute_name("if_condition") %>
41
+ </th>
42
+
43
+ <th class="events-index__events-column events-index__events-column--last_triggered_at">
44
+ <%= Clockface::Event.human_attribute_name("last_triggered_at") %>
45
+ </th>
46
+
47
+ <th class="events-index__events-column events-index__events-column--enabled">
48
+ <%= Clockface::Event.human_attribute_name("enabled") %>
49
+ </th>
50
+
51
+ <th class="events-index__events-column events-index__events-column--edit sorttable_nosort">
52
+ </th>
53
+
54
+ <th class="events-index__events-column events-index__events-column--destroy sorttable_nosort">
55
+ </th>
56
+ </tr>
57
+ </thead>
58
+ <tbody>
59
+ <% @events.each do |event| %>
60
+ <tr class="events-index__events-row" data-id="<%= event.id %>">
61
+ <th scope="row" class="events-index__events-column events-index__events-column--id">
62
+ <%= event.id %>
63
+ </th>
64
+
65
+ <td class="events-index__events-column events-index__events-column--name">
66
+ <%= event.name %>
67
+ </td>
68
+
69
+ <td class="events-index__events-column events-index__events-column--period">
70
+ <%= event.period %>
71
+ </td>
72
+
73
+ <td class="events-index__events-column events-index__events-column--at">
74
+ <%= event.at %>
75
+ </td>
76
+
77
+ <td class="events-index__events-column events-index__events-column--time_zone">
78
+ <%= event.time_zone %>
79
+ </td>
80
+
81
+ <td class="events-index__events-column events-index__events-column--if_condition">
82
+ <%= event.if_condition %>
83
+ </td>
84
+
85
+ <td class="events-index__events-column events-index__events-column--last_triggered_at">
86
+ <%= raw event.last_triggered_at %>
87
+ </td>
88
+
89
+ <td class="events-index__events-column events-index__events-column--enabled" sorttable_customkey="<%= event.enabled? ? 1 : 0 %>">
90
+ <span class="<%= event.enabled? ? "enabled-event" : "disabled-event" %> glyphicon glyphicon-<%= event.enabled? ? "ok" : "remove" %>" aria-hidden="true"></span>
91
+ </td>
92
+
93
+ <td class="events-index__events-column events-index__events-column--edit">
94
+ <%= link_to clockface.edit_event_path(event) do %>
95
+ <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
96
+ <% end %>
97
+ </td>
98
+
99
+ <td class="events-index__events-column events-index__events-column--destroy">
100
+ <%= link_to clockface.event_delete_path(event) do %>
101
+ <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
102
+ <% end %>
103
+ </td>
104
+ </tr>
105
+ <% end %>
106
+ </tbody>
107
+ </table>
108
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="container">
2
+ <div class="events-new__heading-banner">
3
+ <h1>
4
+ <%= link_to clockface.events_path, class: "events-new__heading-banner-link" do %>
5
+ <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
6
+ <% end %>
7
+ <%= t(".heading").downcase %>
8
+ </h1>
9
+ </div>
10
+
11
+ <%= render "clockface/application/flash" %>
12
+
13
+ <%= render "event_form", event: Clockface::Event.new, form_url: clockface.events_path, allow_editing_task: true %>
14
+ </div>
@@ -0,0 +1,57 @@
1
+ <div class="tasks-new__form-container">
2
+ <%= form_for(task, url: form_url, html: { class: "form-horizontal" }) do |form| %>
3
+ <div class="tasks-form__form-element--name form-group">
4
+ <label class="control-label col-sm-2" for="name">
5
+ <%= Clockface::Task.human_attribute_name("name") %>
6
+ </label>
7
+ <div class="col-sm-10">
8
+ <%=
9
+ form.text_field(
10
+ :name,
11
+ class: "form-control",
12
+ placeholder: t(".placeholder.name")
13
+ )
14
+ %>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="tasks-form__form-element--description form-group">
19
+ <label class="control-label col-sm-2" for="description">
20
+ <%= Clockface::Task.human_attribute_name("description") %>
21
+ </label>
22
+ <div class="col-sm-10">
23
+ <%=
24
+ form.text_area(
25
+ :description,
26
+ class: "form-control",
27
+ placeholder: t(".placeholder.description")
28
+ )
29
+ %>
30
+ </div>
31
+ </div>
32
+
33
+ <div class="tasks-form__form-element--command form-group">
34
+ <label class="control-label col-sm-2" for="command">
35
+ <%= Clockface::Task.human_attribute_name("command") %>
36
+ </label>
37
+ <div class="col-sm-10">
38
+ <%=
39
+ form.text_field(
40
+ :command,
41
+ class: "form-control",
42
+ placeholder: t(".placeholder.command")
43
+ )
44
+ %>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="form-group">
49
+ <div class="tasks-new__form-submit col-sm-2">
50
+ <%= link_to clockface.tasks_path do %>
51
+ <button type="button" class="btn btn-default"><%= t(".cancel") %></button>
52
+ <% end %>
53
+ <%= form.submit(t(".submit"), class: "btn btn-success") %>
54
+ </div>
55
+ </div>
56
+ <% end %>
57
+ </div>
@@ -0,0 +1,83 @@
1
+ <div class="container">
2
+ <div class="tasks-edit__heading-banner">
3
+ <h1>
4
+ <%= link_to clockface.tasks_path, class: "tasks-edit__heading-banner-link" do %>
5
+ <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
6
+ <% end %>
7
+ <%= t(".heading").downcase %>
8
+ </h1>
9
+ </div>
10
+
11
+ <%= render "clockface/application/flash" %>
12
+
13
+ <div class="tasks-delete__warning alert alert-danger" role="alert">
14
+ <div class="glyphicon glyphicon-warning-sign"></div>
15
+ <%= t(".warning") %>
16
+ </div>
17
+
18
+ <div class="tasks-delete__task-detail container">
19
+ <div class="tasks-delete__task-detail-element tasks-delete__task-detail-element--id form-group">
20
+ <label class="control-label col-sm-2" for="id">
21
+ <%= Clockface::Task.human_attribute_name("id") %>
22
+ </label>
23
+
24
+ <div class="col-sm-10">
25
+ <%= @task.id %>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="tasks-delete__task-detail-element tasks-delete__task-detail-element--name form-group">
30
+ <label class="control-label col-sm-2" for="name">
31
+ <%= Clockface::Task.human_attribute_name("name") %>
32
+ </label>
33
+
34
+ <div class="col-sm-10">
35
+ <%= @task.name %>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="tasks-delete__task-detail-element tasks-delete__task-detail-element--description form-group">
40
+ <label class="control-label col-sm-2" for="description">
41
+ <%= Clockface::Task.human_attribute_name("description") %>
42
+ </label>
43
+
44
+ <div class="col-sm-10">
45
+ <%= @task.description %>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="tasks-delete__task-detail-element tasks-delete__task-detail-element--command form-group">
50
+ <label class="control-label col-sm-2" for="command">
51
+ <%= Clockface::Task.human_attribute_name("command") %>
52
+ </label>
53
+
54
+ <div class="col-sm-10">
55
+ <%= @task.command %>
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="tasks-delete__captcha-label">
61
+ <%= raw t(".captcha_label", captcha: @captcha) %>
62
+ </div>
63
+
64
+ <%= form_for(@task, url: clockface.task_path(@task), method: "DELETE", html: { class: "form-horizontal" }) do |form| %>
65
+ <%=
66
+ text_field_tag(
67
+ :captcha,
68
+ nil,
69
+ size: Clockface::TasksController::CAPTCHA_LENGTH,
70
+ class: "tasks-delete__form-element--captcha form-control"
71
+ )
72
+ %>
73
+
74
+ <div class="form-group">
75
+ <div class="tasks-delete__form-submit col-sm-2">
76
+ <%= link_to clockface.tasks_path do %>
77
+ <button type="button" class="btn btn-default"><%= t(".cancel") %></button>
78
+ <% end %>
79
+ <%= submit_tag(t(".submit"), class: "btn btn-danger") %>
80
+ </div>
81
+ </div>
82
+ <% end %>
83
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="container">
2
+ <div class="tasks-edit__heading-banner">
3
+ <h1>
4
+ <%= link_to clockface.tasks_path, class: "tasks-edit__heading-banner-link" do %>
5
+ <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
6
+ <% end %>
7
+ <%= t(".heading").downcase %>
8
+ </h1>
9
+ </div>
10
+
11
+ <%= render "clockface/application/flash" %>
12
+
13
+ <%= render "task_form", task: @task, form_url: clockface.task_path(@task) %>
14
+ </div>
@@ -0,0 +1,70 @@
1
+ <div class="container">
2
+ <%= render "clockface/application/flash" %>
3
+
4
+ <div class="tasks-index__heading-banner">
5
+ <h1>
6
+ <%= t(".heading").downcase %>
7
+ </h1>
8
+
9
+ <%= link_to clockface.new_task_path, class: "tasks-index__new-link" do %>
10
+ <button type="button" class="tasks-index__new-btn btn btn-success">
11
+ <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
12
+ </button>
13
+ <% end %>
14
+ </div>
15
+
16
+ <table class="tasks-index__tasks table table-striped sortable">
17
+ <thead class="thead-inverse">
18
+ <tr class="tasks-index__tasks-header-row">
19
+ <th class="tasks-index__tasks-column tasks-index__tasks-column--id">
20
+ <%= Clockface::Task.human_attribute_name("id") %>
21
+ </th>
22
+
23
+ <th class="tasks-index__tasks-column tasks-index__tasks-column--name">
24
+ <%= Clockface::Task.human_attribute_name("name") %>
25
+ </th>
26
+
27
+ <th class="tasks-index__tasks-column tasks-index__tasks-column--description">
28
+ <%= Clockface::Task.human_attribute_name("description") %>
29
+ </th>
30
+
31
+ <th class="tasks-index__tasks-column tasks-index__tasks-column--command">
32
+ <%= Clockface::Task.human_attribute_name("command") %>
33
+ </th>
34
+ </tr>
35
+ </thead>
36
+ <tbody>
37
+ <% @tasks.each do |task| %>
38
+ <tr class="tasks-index__tasks-row" data-id="<%= task.id %>">
39
+ <th scope="row" class="tasks-index__tasks-column tasks-index__tasks-column--id">
40
+ <%= task.id %>
41
+ </th>
42
+
43
+ <td class="tasks-index__tasks-column tasks-index__tasks-column--name">
44
+ <%= task.name %>
45
+ </td>
46
+
47
+ <td class="tasks-index__tasks-column tasks-index__tasks-column--description">
48
+ <%= task.description %>
49
+ </td>
50
+
51
+ <td class="tasks-index__tasks-column tasks-index__tasks-column--command">
52
+ <%= task.command %>
53
+ </td>
54
+
55
+ <td class="tasks-index__tasks-column tasks-index__tasks-column--edit">
56
+ <%= link_to clockface.edit_task_path(task) do %>
57
+ <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
58
+ <% end %>
59
+ </td>
60
+
61
+ <td class="tasks-index__tasks-column tasks-index__tasks-column--destroy">
62
+ <%= link_to clockface.task_delete_path(task) do %>
63
+ <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
64
+ <% end %>
65
+ </td>
66
+ </tr>
67
+ <% end %>
68
+ </tbody>
69
+ </table>
70
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="container">
2
+ <div class="tasks-new__heading-banner">
3
+ <h1>
4
+ <%= link_to clockface.tasks_path, class: "tasks-new__heading-banner-link" do %>
5
+ <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
6
+ <% end %>
7
+ <%= t(".heading").downcase %>
8
+ </h1>
9
+ </div>
10
+
11
+ <%= render "clockface/application/flash" %>
12
+
13
+ <%= render "task_form", task: Clockface::Task.new, form_url: clockface.tasks_path %>
14
+ </div>
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Clockface</title>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
7
+ <%= stylesheet_link_tag "clockface/application", media: "all" %>
8
+ <%= javascript_include_tag "clockface/application" %>
9
+ <%= csrf_meta_tags %>
10
+
11
+ <% ["Raleway", "Open+Sans"].each do |family| %>
12
+ <%=
13
+ stylesheet_link_tag(
14
+ "http://fonts.googleapis.com/css?family=#{family}",
15
+ type: "text/css"
16
+ )
17
+ %>
18
+ <% end %>
19
+ </head>
20
+ <body id="page-<%= params[:controller] %>-<%= params[:action] %>">
21
+
22
+ <%= render "nav" %>
23
+ <%= yield %>
24
+ <%= render "footer" %>
25
+
26
+ </body>
27
+ </html>
@@ -0,0 +1,158 @@
1
+ en:
2
+ datetime:
3
+ formats:
4
+ international: "%Y-%m-%d %H:%M <span>%Z</span>"
5
+ distance_in_words:
6
+ # Core I18n doesn't define values for hours and weeks
7
+ x_hours:
8
+ one: 1 hour
9
+ other: "%{count} hours"
10
+ x_weeks:
11
+ one: 1 week
12
+ other: "%{count} weeks"
13
+ units:
14
+ seconds: Seconds
15
+ minutes: Minutes
16
+ hours: Hours
17
+ days: Days
18
+ weeks: Weeks
19
+ months: Months
20
+ years: Years
21
+
22
+ activerecord:
23
+ attributes:
24
+ clockface/event:
25
+ id: ID
26
+ enabled: Enabled
27
+ tenant: Tenant
28
+ last_triggered_at: Last Triggered At
29
+ name: Task
30
+ period: Every
31
+ at: At
32
+ time_zone: Time Zone
33
+ if_condition: Only run on...
34
+ clockface/event/if_condition:
35
+ even_week: even weeks
36
+ odd_week: odd weeks
37
+ weekday: weekdays
38
+ first_of_month: first day of the month
39
+ last_of_month: last day of the month
40
+ clockface/task:
41
+ id: ID
42
+ name: Name
43
+ description: Description
44
+ command: Command
45
+
46
+ errors:
47
+ models:
48
+ clockface/event:
49
+ attributes:
50
+ tenant:
51
+ invalid: Must be the name of the tenant
52
+ present: Must be blank
53
+ period_value:
54
+ blank: "Please specify a %{attribute}"
55
+ greater_than: "%{attribute} must be greater than %{count}"
56
+ period_units:
57
+ blank: "Please specify a %{attribute}"
58
+ inclusion: Invalid value for %{attribute}
59
+ day_of_week:
60
+ inclusion: Invalid value for %{attribute}
61
+ day_of_week_must_have_timestamp: "Day of week can not be specified without a time"
62
+ hour:
63
+ inclusion: Invalid value for %{attribute}
64
+ minute:
65
+ inclusion: Invalid value for %{attribute}
66
+ time_zone:
67
+ inclusion: Invalid value for %{attribute}
68
+ if_condition:
69
+ inclusion: Invalid value for %{attribute}
70
+ clockface/task:
71
+ attributes:
72
+ name:
73
+ blank: "Please specify a %{attribute}"
74
+ description: null
75
+ command:
76
+ blank: "Please specify a %{attribute}"
77
+
78
+ clockface:
79
+ application:
80
+ nav:
81
+ heading: Clockface
82
+ links:
83
+ tasks: Tasks
84
+ events: Events
85
+ footer:
86
+ need_help_html: "Have a question? <a href=\"%{href}\" target=\"_blank\">Ask here</a>"
87
+ events:
88
+ create:
89
+ duplicate_event: Another event with this configuration already exists
90
+ success: Event created successfully
91
+ delete:
92
+ heading: Delete Event
93
+ cancel: Cancel
94
+ captcha_label: "Please enter the code <span>%{captcha}</span> to confirm deletion"
95
+ submit: Delete
96
+ validation:
97
+ invalid_id: Event not found
98
+ warning: "Deleting a event is irreversible!"
99
+ destroy:
100
+ event_not_found: Unknown event id %{id}
101
+ success: Event deleted successfully
102
+ failure: Whoops, something went wrong! Please contact your site administrator
103
+ validation:
104
+ incorrect_captcha: Please enter the correct code to confirm deletion
105
+ edit:
106
+ heading: Edit Event
107
+ validation:
108
+ invalid_id: Event not found
109
+ index:
110
+ heading: Events
111
+ event_form:
112
+ submit: Save
113
+ cancel: Cancel
114
+ new:
115
+ heading: New Event
116
+ update:
117
+ duplicate_event: Another event with this configuration already exists
118
+ event_not_found: Unknown event id %{id}
119
+ success: Event updated successfully
120
+ tasks:
121
+ create:
122
+ success: Task created successfully
123
+ delete:
124
+ heading: Delete Task
125
+ cancel: Cancel
126
+ captcha_label: "Please enter the code <span>%{captcha}</span> to confirm deletion"
127
+ submit: Delete
128
+ validation:
129
+ invalid_id: Task not found
130
+ warning: "Deleting a task is irreversible!"
131
+ destroy:
132
+ task_not_found: Unknown task id %{id}
133
+ success: Task deleted successfully
134
+ failure: Whoops, something went wrong! Please contact your site administrator
135
+ validation:
136
+ incorrect_captcha: Please enter the correct code to confirm deletion
137
+ events_exist:
138
+ one: "Error - 1 scheduled event exists for this task"
139
+ other: "Error - %{count} scheduled events exist for this task"
140
+ edit:
141
+ heading: Edit Task
142
+ validation:
143
+ invalid_id: Task not found
144
+ index:
145
+ heading: Tasks
146
+ task_form:
147
+ placeholder:
148
+ name: e.g. Send RSVP Reminders
149
+ description: e.g. Send emails to users that have RSVP'd for an event
150
+ command: "e.g. {\"class\":\"EventReminderWorker\"}"
151
+ submit: Save
152
+ cancel: Cancel
153
+ new:
154
+ heading: New Task
155
+ update:
156
+ task_not_found: Unknown event id %{id}
157
+ success: Task updated successfully
158
+