decidim-time_tracker 0.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 (134) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +174 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/config/admin/decidim_time_tracker_manifest.css +2 -0
  6. data/app/assets/config/admin/decidim_time_tracker_manifest.js +1 -0
  7. data/app/assets/config/decidim_time_tracker_manifest.css +3 -0
  8. data/app/assets/config/decidim_time_tracker_manifest.js +1 -0
  9. data/app/assets/images/decidim/time_tracker/icon.svg +1 -0
  10. data/app/assets/javascripts/decidim/time_tracker/activity_ui.js.es6 +112 -0
  11. data/app/assets/javascripts/decidim/time_tracker/admin/assignations.js.es6 +35 -0
  12. data/app/assets/javascripts/decidim/time_tracker/reports/reports.js.es6 +19 -0
  13. data/app/assets/javascripts/decidim/time_tracker/time_tracker.js.es6 +82 -0
  14. data/app/assets/javascripts/decidim/time_tracker/timer_api.js.es6 +55 -0
  15. data/app/assets/stylesheets/decidim/time_tracker/_variables.scss +1 -0
  16. data/app/assets/stylesheets/decidim/time_tracker/admin/time_tracker.scss +59 -0
  17. data/app/assets/stylesheets/decidim/time_tracker/time_tracker.scss +118 -0
  18. data/app/cells/decidim/time_tracker/milestone/show.erb +42 -0
  19. data/app/cells/decidim/time_tracker/milestone_cell.rb +54 -0
  20. data/app/commands/decidim/time_tracker/admin/create_activity.rb +39 -0
  21. data/app/commands/decidim/time_tracker/admin/create_assignation.rb +75 -0
  22. data/app/commands/decidim/time_tracker/admin/create_task.rb +33 -0
  23. data/app/commands/decidim/time_tracker/admin/create_time_tracker.rb +76 -0
  24. data/app/commands/decidim/time_tracker/admin/destroy_activity.rb +43 -0
  25. data/app/commands/decidim/time_tracker/admin/destroy_assignation.rb +43 -0
  26. data/app/commands/decidim/time_tracker/admin/destroy_task.rb +43 -0
  27. data/app/commands/decidim/time_tracker/admin/export_time_tracker.rb +150 -0
  28. data/app/commands/decidim/time_tracker/admin/update_activity.rb +49 -0
  29. data/app/commands/decidim/time_tracker/admin/update_assignation.rb +42 -0
  30. data/app/commands/decidim/time_tracker/admin/update_task.rb +44 -0
  31. data/app/commands/decidim/time_tracker/create_milestone.rb +57 -0
  32. data/app/commands/decidim/time_tracker/create_request_assignation.rb +39 -0
  33. data/app/commands/decidim/time_tracker/start_time_event.rb +104 -0
  34. data/app/commands/decidim/time_tracker/stop_last_time_event.rb +45 -0
  35. data/app/controllers/concerns/decidim/time_tracker/admin/filterable_assignations.rb +50 -0
  36. data/app/controllers/decidim/time_tracker/activity_questionnaire_controller.rb +76 -0
  37. data/app/controllers/decidim/time_tracker/admin/activities_controller.rb +82 -0
  38. data/app/controllers/decidim/time_tracker/admin/activity_questionnaire_controller.rb +52 -0
  39. data/app/controllers/decidim/time_tracker/admin/application_controller.rb +22 -0
  40. data/app/controllers/decidim/time_tracker/admin/assignations_controller.rb +84 -0
  41. data/app/controllers/decidim/time_tracker/admin/assignee_questionnaire_controller.rb +58 -0
  42. data/app/controllers/decidim/time_tracker/admin/stats_controller.rb +24 -0
  43. data/app/controllers/decidim/time_tracker/admin/tasks_controller.rb +112 -0
  44. data/app/controllers/decidim/time_tracker/admin/time_tracker_exports_controller.rb +25 -0
  45. data/app/controllers/decidim/time_tracker/application_controller.rb +24 -0
  46. data/app/controllers/decidim/time_tracker/assignations_controller.rb +38 -0
  47. data/app/controllers/decidim/time_tracker/assignee_questionnaire_controller.rb +80 -0
  48. data/app/controllers/decidim/time_tracker/milestones_controller.rb +62 -0
  49. data/app/controllers/decidim/time_tracker/reports/user_controller.rb +38 -0
  50. data/app/controllers/decidim/time_tracker/time_events_controller.rb +68 -0
  51. data/app/controllers/decidim/time_tracker/time_tracker_controller.rb +43 -0
  52. data/app/forms/decidim/time_tracker/admin/activity_form.rb +27 -0
  53. data/app/forms/decidim/time_tracker/admin/assignation_form.rb +29 -0
  54. data/app/forms/decidim/time_tracker/admin/task_form.rb +22 -0
  55. data/app/forms/decidim/time_tracker/milestone_form.rb +32 -0
  56. data/app/forms/decidim/time_tracker/time_event_form.rb +47 -0
  57. data/app/helpers/decidim/time_tracker/admin/application_helper.rb +33 -0
  58. data/app/helpers/decidim/time_tracker/application_helper.rb +95 -0
  59. data/app/jobs/decidim/time_tracker/stop_counter_job.rb +29 -0
  60. data/app/models/decidim/time_tracker/activity.rb +135 -0
  61. data/app/models/decidim/time_tracker/application_record.rb +10 -0
  62. data/app/models/decidim/time_tracker/assignation.rb +68 -0
  63. data/app/models/decidim/time_tracker/assignee.rb +29 -0
  64. data/app/models/decidim/time_tracker/assignee_data.rb +19 -0
  65. data/app/models/decidim/time_tracker/milestone.rb +27 -0
  66. data/app/models/decidim/time_tracker/task.rb +44 -0
  67. data/app/models/decidim/time_tracker/time_event.rb +62 -0
  68. data/app/models/decidim/time_tracker/time_tracker.rb +46 -0
  69. data/app/models/decidim/time_tracker/tos_acceptance.rb +16 -0
  70. data/app/permissions/decidim/time_tracker/admin/permissions.rb +69 -0
  71. data/app/permissions/decidim/time_tracker/permissions.rb +69 -0
  72. data/app/presenters/decidim/time_tracker/admin_log/activity_presenter.rb +44 -0
  73. data/app/presenters/decidim/time_tracker/admin_log/assignation_presenter.rb +48 -0
  74. data/app/presenters/decidim/time_tracker/admin_log/task_presenter.rb +46 -0
  75. data/app/presenters/decidim/time_tracker/admin_log/value_types/activity_presenter.rb +28 -0
  76. data/app/presenters/decidim/time_tracker/admin_log/value_types/time_tracker_presenter.rb +28 -0
  77. data/app/views/decidim/time_tracker/admin/_breadcrumbs.html.erb +56 -0
  78. data/app/views/decidim/time_tracker/admin/activities/_form.html.erb +41 -0
  79. data/app/views/decidim/time_tracker/admin/activities/_index.html.erb +73 -0
  80. data/app/views/decidim/time_tracker/admin/activities/edit.html.erb +5 -0
  81. data/app/views/decidim/time_tracker/admin/activities/new.html.erb +5 -0
  82. data/app/views/decidim/time_tracker/admin/assignations/_form.html.erb +30 -0
  83. data/app/views/decidim/time_tracker/admin/assignations/index.html.erb +67 -0
  84. data/app/views/decidim/time_tracker/admin/assignations/new.html.erb +9 -0
  85. data/app/views/decidim/time_tracker/admin/questionnaires/_index.html.erb +27 -0
  86. data/app/views/decidim/time_tracker/admin/questionnaires/_questionnaire.html.erb +21 -0
  87. data/app/views/decidim/time_tracker/admin/stats/index.html.erb +50 -0
  88. data/app/views/decidim/time_tracker/admin/tasks/_assignations.html.erb +60 -0
  89. data/app/views/decidim/time_tracker/admin/tasks/_form.html.erb +7 -0
  90. data/app/views/decidim/time_tracker/admin/tasks/_index.html.erb +61 -0
  91. data/app/views/decidim/time_tracker/admin/tasks/edit.html.erb +16 -0
  92. data/app/views/decidim/time_tracker/admin/tasks/index.html.erb +5 -0
  93. data/app/views/decidim/time_tracker/admin/tasks/new.html.erb +12 -0
  94. data/app/views/decidim/time_tracker/milestones/index.html.erb +60 -0
  95. data/app/views/decidim/time_tracker/reports/user/index.html.erb +41 -0
  96. data/app/views/decidim/time_tracker/time_tracker/_activity.html.erb +43 -0
  97. data/app/views/decidim/time_tracker/time_tracker/_answer_questionnaire.html.erb +7 -0
  98. data/app/views/decidim/time_tracker/time_tracker/_assignee_data.html.erb +9 -0
  99. data/app/views/decidim/time_tracker/time_tracker/_callout_status.html.erb +3 -0
  100. data/app/views/decidim/time_tracker/time_tracker/_idle_activity.html.erb +27 -0
  101. data/app/views/decidim/time_tracker/time_tracker/_milestone_form.html.erb +35 -0
  102. data/app/views/decidim/time_tracker/time_tracker/_request_activity.html.erb +22 -0
  103. data/app/views/decidim/time_tracker/time_tracker/_sign_in.html.erb +5 -0
  104. data/app/views/decidim/time_tracker/time_tracker/index.html.erb +59 -0
  105. data/config/activity_questionnaire.yml +26 -0
  106. data/config/assignee_questionnaire.yml +37 -0
  107. data/config/i18n-tasks.yml +10 -0
  108. data/config/locales/ca.yml +394 -0
  109. data/config/locales/cs.yml +394 -0
  110. data/config/locales/en.yml +419 -0
  111. data/config/locales/es.yml +394 -0
  112. data/db/migrate/20200302115642_create_decidim_time_tracker_time_trackers.rb +10 -0
  113. data/db/migrate/20200302115654_create_decidim_time_tracker_tasks.rb +11 -0
  114. data/db/migrate/20200302115658_create_decidim_time_tracker_assignees.rb +12 -0
  115. data/db/migrate/20200302115700_create_decidim_time_tracker_activities.rb +19 -0
  116. data/db/migrate/20200302115708_create_decidim_time_tracker_milestones.rb +13 -0
  117. data/db/migrate/20200302115831_create_decidim_time_tracker_assignations.rb +16 -0
  118. data/db/migrate/20200302115840_create_decidim_time_tracker_time_events.rb +15 -0
  119. data/db/migrate/20201118153327_create_decidim_time_tracker_assignee_data.rb +10 -0
  120. data/db/migrate/20201223115130_create_decidim_time_tracker_tos_acceptances.rb +18 -0
  121. data/lib/decidim/time_tracker.rb +45 -0
  122. data/lib/decidim/time_tracker/admin.rb +10 -0
  123. data/lib/decidim/time_tracker/admin_engine.rb +51 -0
  124. data/lib/decidim/time_tracker/component.rb +248 -0
  125. data/lib/decidim/time_tracker/engine.rb +59 -0
  126. data/lib/decidim/time_tracker/reports.rb +10 -0
  127. data/lib/decidim/time_tracker/reports_engine.rb +34 -0
  128. data/lib/decidim/time_tracker/test/admin_log_presenter_examples.rb +34 -0
  129. data/lib/decidim/time_tracker/test/factories.rb +110 -0
  130. data/lib/decidim/time_tracker/test/time_tracker_context.rb +18 -0
  131. data/lib/decidim/time_tracker/time_tracker_activity_questionnaire_answers_serializer.rb +74 -0
  132. data/lib/decidim/time_tracker/version.rb +9 -0
  133. data/vendor/assets/javascripts/jsrender.min.js +4 -0
  134. metadata +276 -0
@@ -0,0 +1,55 @@
1
+ // Controlls time events
2
+
3
+ class TimerApi { // eslint-disable-line no-unused-vars
4
+ constructor(startEndpoint, stopEndpoint) {
5
+ this.startEndpoint = startEndpoint;
6
+ this.stopEndpoint = stopEndpoint;
7
+ this.csrfToken = $('meta[name=csrf-token]').attr('content');
8
+ }
9
+
10
+ start() {
11
+ const promise = $.Deferred();
12
+
13
+ $.ajax({
14
+ method: "POST",
15
+ url: this.startEndpoint,
16
+ contentType: "application/json",
17
+ headers: {
18
+ 'X-CSRF-Token': this.csrfToken
19
+ },
20
+ success: (data) => {
21
+ promise.resolve(data);
22
+ },
23
+ error: (jq) => {
24
+ const error = (jq.responseJSON && jq.responseJSON.error) || "Unkown error starting timer";
25
+ console.error("error starting time", error);
26
+ promise.reject(error);
27
+ }
28
+ });
29
+
30
+ return promise;
31
+ }
32
+
33
+ stop() {
34
+ const promise = $.Deferred();
35
+
36
+ $.ajax({
37
+ method: "POST",
38
+ url: this.stopEndpoint,
39
+ contentType: "application/json",
40
+ headers: {
41
+ 'X-CSRF-Token': this.csrfToken
42
+ },
43
+ success: (data) => {
44
+ promise.resolve(data);
45
+ },
46
+ error: (jq) => {
47
+ const error = (jq.responseJSON && jq.responseJSON.error) || "Unkown error stopping timer";
48
+ console.error("error stopping time", error);
49
+ promise.reject(error);
50
+ }
51
+ });
52
+
53
+ return promise;
54
+ }
55
+ }
@@ -0,0 +1,59 @@
1
+ @import "decidim/variables";
2
+
3
+ .action-icon {
4
+ &-success {
5
+ .icon {
6
+ color: $success;
7
+ }
8
+ }
9
+ }
10
+
11
+ .breadcrumbs {
12
+ margin-bottom: 0;
13
+
14
+ li {
15
+ font-size: 1em;
16
+ }
17
+ }
18
+
19
+ .activity--inactive {
20
+ opacity: 0.5;
21
+ }
22
+
23
+ .assignation- {
24
+ &-pending {
25
+ .assignation--status {
26
+ background-color: $warning;
27
+ }
28
+ td {
29
+ background-color: transparentize($warning, 0.9);
30
+ }
31
+ }
32
+ &-accepted {
33
+ .assignation--status {
34
+ background-color: $success;
35
+ }
36
+ td {
37
+ background-color: transparentize($success, 0.9);
38
+ }
39
+ }
40
+
41
+ &-rejected td {
42
+ opacity: 0.5;
43
+ }
44
+ }
45
+
46
+ .help-text + .form-error {
47
+ margin-top: 0rem;
48
+ }
49
+
50
+ .time-tracker {
51
+ &--clock {
52
+ span:not(:last-child)::after {
53
+ content: " ";
54
+ }
55
+ .text-muted {
56
+ opacity: 0.5;
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,118 @@
1
+ @import "decidim/variables";
2
+ @import "decidim/utils/settings";
3
+ @import "decidim/utils/mixins";
4
+ @import "decidim/time_tracker/variables";
5
+
6
+ .time-tracker {
7
+ &-activity,
8
+ &-request {
9
+ background-color: white;
10
+ border: 1px solid #eee;
11
+ padding: 1rem;
12
+ margin-bottom: 1rem;
13
+ h2 {
14
+ color: var(--secondary);
15
+ }
16
+ .content {
17
+ @media only screen and (min-width: 38em) {
18
+ display: grid;
19
+ grid-auto-flow: column;
20
+ grid-template-columns: 66.6% 33.3%;
21
+ }
22
+ @media only screen and (min-width: 50em) {
23
+ grid-template-columns: 75% 25%;
24
+ }
25
+ .left,
26
+ .right {
27
+ display: flex;
28
+ flex-direction: column;
29
+ }
30
+ .top {
31
+ flex: 1;
32
+ }
33
+ .bottom {
34
+ padding: 0.5em 0;
35
+ text-align: center;
36
+ }
37
+ }
38
+ .tracker-buttons {
39
+ text-align: right;
40
+ .button {
41
+ margin-left: 1rem;
42
+ }
43
+ .button:last-child {
44
+ margin-left: 0;
45
+ }
46
+ }
47
+ .callout {
48
+ margin: 0;
49
+ }
50
+ .elapsed-time {
51
+ width: max-content;
52
+ .elapsed-time-clock {
53
+ font-size: 2rem;
54
+ }
55
+ }
56
+ .milestone {
57
+ margin: 1em;
58
+ padding-top: 1em;
59
+ border-top: 1px solid #eee;
60
+ }
61
+ }
62
+ &--report {
63
+ .card--list__annotation {
64
+ margin-right: 1rem;
65
+ }
66
+ .card--list__heading {
67
+ margin-bottom: 0.5rem;
68
+ }
69
+ }
70
+ &--clock {
71
+ span:not(:last-child)::after {
72
+ content: " ";
73
+ }
74
+ .text-muted {
75
+ opacity: 0.5;
76
+ }
77
+ }
78
+ &--activity {
79
+ &:hover {
80
+ .card--list__item {
81
+ transition: background-color 200ms;
82
+ background-color: #fbfbfb;
83
+ }
84
+ }
85
+ }
86
+ &--assignee-data {
87
+ margin-bottom: 3rem;
88
+ }
89
+ &--start-button {
90
+ margin-top: 2rem;
91
+ margin-bottom: 1rem;
92
+ &-wrapper {
93
+ display: flex;
94
+ justify-content: center;
95
+ align-items: center;
96
+ }
97
+ }
98
+ }
99
+
100
+ .card--milestone {
101
+ .card__image.empty {
102
+ background: $light-gray;
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ & > svg {
107
+ fill: $medium-gray;
108
+ font-size: 2rem;
109
+ }
110
+ }
111
+ }
112
+
113
+ .show-milestones {
114
+ color: $secondary;
115
+ &:hover {
116
+ text-decoration: underline;
117
+ }
118
+ }
@@ -0,0 +1,42 @@
1
+ <div class="column">
2
+ <article class="card card--milestone">
3
+ <%= image %>
4
+ <div class="card__content">
5
+ <div class="card__header">
6
+ <%= title %>
7
+ </div>
8
+
9
+ <div class="card__text">
10
+ <div class="card__text--paragraph">
11
+ <% if list? %>
12
+ <h5>
13
+ <%= model.title %>
14
+ </h5>
15
+ <% end %>
16
+
17
+ <div class="text-ellipsis">
18
+ <%= model.description %>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+
24
+ <div class="card__footer">
25
+ <div class="card__support">
26
+ <% if list? %>
27
+ <div class="text"> <%= t(".time_dedicated") %></div>
28
+ <div class="text">
29
+ <strong><%= clockify_seconds(seconds_elapsed) %></strong>
30
+ </div>
31
+ <% else %>
32
+ <div class="text">
33
+ <%= model.created_at.strftime("%d %b %Y") %>
34
+ </div>
35
+ <div class="text">
36
+ <%= model.created_at.strftime("%H:%M") %>
37
+ </div>
38
+ <% end %>
39
+ </div>
40
+ </div>
41
+ </article>
42
+ </div>
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ class MilestoneCell < Decidim::ViewModel
6
+ include Decidim::SanitizeHelper
7
+ include Decidim::CardHelper
8
+ include Decidim::IconHelper
9
+ include Decidim::TimeTracker::ApplicationHelper
10
+
11
+ view_paths << "#{Decidim::TimeTracker::Engine.root}/app/cells/decidim/time_tracker/milestone"
12
+
13
+ def show
14
+ render
15
+ end
16
+
17
+ def list?
18
+ options[:type] == :list
19
+ end
20
+
21
+ def title
22
+ return content_tag :strong, model.title unless list?
23
+
24
+ link_to milestones_path do
25
+ content_tag :h4, class: "card__title" do
26
+ t("title", user_name: model.user.name, scope: "decidim.time_tracker.milestone")
27
+ end
28
+ end
29
+ end
30
+
31
+ def image
32
+ image_url = model.attachments&.first&.url
33
+
34
+ if image_url.present?
35
+ link_to milestones_path, class: "card__link" do
36
+ image_tag image_url, class: "card__image"
37
+ end
38
+ else
39
+ content_tag :div, class: "card__image empty" do
40
+ icon "timer"
41
+ end
42
+ end
43
+ end
44
+
45
+ def seconds_elapsed
46
+ @seconds_elapsed ||= model.activity.user_seconds_elapsed(model.user)
47
+ end
48
+
49
+ def milestones_path
50
+ Decidim::EngineRouter.main_proxy(model.activity.task.component).milestones_path(nickname: model.user.nickname)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ module Admin
6
+ # A command with all the business logic when creating a activity
7
+ class CreateActivity < Rectify::Command
8
+ def initialize(form, task)
9
+ @form = form
10
+ @task = task
11
+ end
12
+
13
+ # Creates the activity if valid.
14
+ #
15
+ # Broadcasts :ok if successful, :invalid otherwise.
16
+ def call
17
+ return broadcast(:invalid) if @form.invalid?
18
+
19
+ create_activity
20
+ broadcast(:ok)
21
+ end
22
+
23
+ def create_activity
24
+ Decidim.traceability.create!(
25
+ Decidim::TimeTracker::Activity,
26
+ @form.current_user,
27
+ description: @form.description,
28
+ active: @form.active,
29
+ start_date: @form.start_date,
30
+ end_date: @form.end_date,
31
+ max_minutes_per_day: @form.max_minutes_per_day,
32
+ requests_start_at: @form.requests_start_at,
33
+ task: @task
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ module Admin
6
+ # A command with all the business logic when creating a assignation
7
+ class CreateAssignation < Rectify::Command
8
+ def initialize(form, activity)
9
+ @form = form
10
+ @activity = activity
11
+ end
12
+
13
+ # Creates the assignation if valid.
14
+ #
15
+ # Broadcasts :ok if successful, :invalid otherwise.
16
+ def call
17
+ return broadcast(:invalid) if @form.invalid?
18
+
19
+ ActiveRecord::Base.transaction do
20
+ @user ||= existing_user || new_user
21
+ create_assignation
22
+ end
23
+
24
+ broadcast(:ok)
25
+ end
26
+
27
+ def create_assignation
28
+ Decidim.traceability.create!(
29
+ Decidim::TimeTracker::Assignation,
30
+ @form.current_user,
31
+ activity: @activity,
32
+ user: @user,
33
+ status: :accepted,
34
+ invited_at: Time.current,
35
+ invited_by_user: @form.current_user
36
+ )
37
+ end
38
+
39
+ def existing_user
40
+ return @existing_user if defined?(@existing_user)
41
+
42
+ if @form.existing_user
43
+ @existing_user = @form.user
44
+ else
45
+ @existing_user = User.find_by(
46
+ email: @form.email,
47
+ organization: @activity.task.time_tracker.component.organization
48
+ )
49
+
50
+ InviteUserAgain.call(@existing_user, "invite_private_user") if @existing_user && !@existing_user.invitation_accepted?
51
+ end
52
+
53
+ @existing_user
54
+ end
55
+
56
+ def new_user
57
+ @new_user ||= InviteUser.call(user_form) do
58
+ on(:ok) do |user|
59
+ return user
60
+ end
61
+ end
62
+ end
63
+
64
+ def user_form
65
+ OpenStruct.new(name: @form.name,
66
+ email: @form.email.downcase,
67
+ organization: @activity.task.time_tracker.component.organization,
68
+ admin: false,
69
+ invited_by: @form.current_user,
70
+ invitation_instructions: "invite_private_user")
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end