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,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ module Admin
6
+ # Custom helpers, scoped to the time_tracker engine.
7
+ #
8
+ module ApplicationHelper
9
+ def change_assignation_status_button(assignation, success_path: nil)
10
+ path_reject = task_activity_assignation_path(assignation.task, assignation.activity, assignation, assignation_status: :rejected, success_path: success_path)
11
+ path_accept = task_activity_assignation_path(assignation.task, assignation.activity, assignation, assignation_status: :accepted, success_path: success_path)
12
+ label_reject = t("assignations.actions.reject", scope: "decidim.time_tracker.admin")
13
+ label_accept = t("assignations.actions.accept", scope: "decidim.time_tracker.admin")
14
+
15
+ icons = [
16
+ assignation.pending? || assignation.accepted? ? icon_link_to("x", path_reject, label_reject, method: :patch, class: "action-icon--status") : empty_icon,
17
+ assignation.pending? || assignation.rejected? ? icon_link_to("check", path_accept, label_accept, method: :patch, class: "action-icon--status") : empty_icon
18
+ ]
19
+
20
+ safe_join(icons)
21
+ end
22
+
23
+ def empty_icon
24
+ content_tag :a, class: "action-icon" do
25
+ content_tag :span do
26
+ icon("", aria_label: "", role: "img")
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ # Custom helpers, scoped to the time_tracker engine.
6
+ #
7
+ module ApplicationHelper
8
+ include Decidim::TranslatableAttributes
9
+
10
+ def milestones_path(params = {})
11
+ Decidim::EngineRouter.main_proxy(current_component).milestones_path(params)
12
+ end
13
+
14
+ def tasks_label
15
+ translated_attribute(component_settings.tasks_label).presence || t("models.task.name", scope: "decidim.time_tracker")
16
+ end
17
+
18
+ def activities_label
19
+ translated_attribute(component_settings.activities_label).presence || t("models.activity.name", scope: "decidim.time_tracker")
20
+ end
21
+
22
+ def assignations_label
23
+ translated_attribute(component_settings.assignations_label).presence || t("models.assignation.name", scope: "decidim.time_tracker")
24
+ end
25
+
26
+ def time_events_label
27
+ translated_attribute(component_settings.time_events_label).presence || t("models.time_entry.name", scope: "decidim.time_tracker")
28
+ end
29
+
30
+ def milestones_label
31
+ translated_attribute(component_settings.milestones_label).presence || t("models.milestone.name", scope: "decidim.time_tracker")
32
+ end
33
+
34
+ # turns a number of seconds to a string 0h 0m 0s
35
+ def clockify_seconds(total_seconds, padded: false)
36
+ total_seconds = total_seconds.to_i
37
+
38
+ clock = {
39
+ hours: total_seconds / (60 * 60),
40
+ minutes: (total_seconds / 60) % 60,
41
+ seconds: total_seconds % 60
42
+ }
43
+
44
+ content_tag :span, class: "time-tracker--clock" do
45
+ safe_join(
46
+ clock.map do |label, value|
47
+ string_value = padded ? value.to_s.rjust(2, "0") : value
48
+ content_tag(:span, t("decidim.time_tracker.clock.#{label}", n: string_value), class: ("text-muted" if value.zero?))
49
+ end
50
+ )
51
+ end
52
+ end
53
+
54
+ def assignation_status_label(status)
55
+ klass = case status
56
+ when "accepted" then "success"
57
+ when "pending" then "warning"
58
+ when "rejected" then "danger"
59
+ end
60
+
61
+ content_tag :span, class: "#{klass} label" do
62
+ t("models.assignation.fields.statuses.#{status}", scope: "decidim.time_tracker")
63
+ end
64
+ end
65
+
66
+ def assignation_date(assignation)
67
+ if assignation.invited_at.present?
68
+ t("models.assignation.fields.invited_at", time: l(assignation.invited_at, format: :short), scope: "decidim.time_tracker")
69
+ elsif assignation.requested_at.present?
70
+ t("models.assignation.fields.requested_at", time: l(assignation.requested_at, format: :short), scope: "decidim.time_tracker")
71
+ end
72
+ end
73
+
74
+ def user_total_time_dedicated(user)
75
+ Assignation.where(user: user).sum(&:time_dedicated)
76
+ end
77
+
78
+ def user_joined_at(user)
79
+ Assignee.for(user).tos_accepted_at(time_tracker)
80
+ end
81
+
82
+ def user_last_milestone(user)
83
+ Milestone.where(user: user).order(created_at: :desc).first
84
+ end
85
+
86
+ def must_fill_in_data?
87
+ !current_assignee.tos_accepted?(time_tracker) && !activities_empty?
88
+ end
89
+
90
+ def activities_empty?
91
+ time_tracker.activities.active.empty?
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This jobs stops a counter if is over timed
5
+ module TimeTracker
6
+ class StopCounterJob < ApplicationJob
7
+ queue_as :default
8
+
9
+ # time_event - the counter to stop, only stopped if it's due time is reached
10
+ def perform(time_event)
11
+ @time_event = time_event
12
+ return if @time_event.stopped?
13
+ return if time_available?
14
+
15
+ @time_event.stop!
16
+ end
17
+
18
+ private
19
+
20
+ def time_available?
21
+ date = Time.zone.at(@time_event.start)
22
+ last_elapsed = @time_event.seconds_elapsed
23
+ remaining = @time_event.activity.user_remaining_for_date(@time_event.user, date)
24
+
25
+ remaining > last_elapsed
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ # The data store for a Activity in the Decidim::TimeTracker component. It
6
+ # stores a description and other useful information related to an activity.
7
+ class Activity < ApplicationRecord
8
+ include Decidim::Traceable
9
+ include Decidim::Loggable
10
+
11
+ self.table_name = :decidim_time_tracker_activities
12
+
13
+ belongs_to :task,
14
+ class_name: "Decidim::TimeTracker::Task"
15
+
16
+ has_many :assignations,
17
+ class_name: "Decidim::TimeTracker::Assignation",
18
+ dependent: :destroy
19
+
20
+ has_many :time_events,
21
+ class_name: "Decidim::TimeTracker::TimeEvent"
22
+
23
+ has_many :milestones,
24
+ class_name: "Decidim::TimeTracker::Milestone"
25
+
26
+ scope :active, -> { where(active: true) }
27
+
28
+ delegate :questionnaire, to: :task
29
+
30
+ # total number of seconds spent by the user
31
+ # not counting current counters
32
+ def user_total_seconds(user)
33
+ time_events.where(user: user).sum(&:total_seconds).to_i
34
+ end
35
+
36
+ def user_total_seconds_for_date(user, date)
37
+ time_events.started_between(date.beginning_of_day, date.end_of_day).where(user: user).sum(&:total_seconds).to_i
38
+ end
39
+
40
+ # Total number of seconds spent by the user
41
+ # and counting any possible running counters
42
+ def user_seconds_elapsed(user)
43
+ total = user_total_seconds(user)
44
+ total += last_event_for(user).seconds_elapsed if last_event_for(user)
45
+ total
46
+ end
47
+
48
+ def counter_active_for?(user)
49
+ return false unless last_event_for(user)
50
+
51
+ !last_event_for(user).stopped?
52
+ end
53
+
54
+ # Returns how many seconds are available for this task in the current day
55
+ # this can be less than the activity is allowed due the change of date
56
+ def remaining_seconds_for_today
57
+ @remaining_seconds_for_today ||= [max_minutes_per_day * 60, (Time.current.end_of_day - Time.current).to_i].min
58
+ end
59
+
60
+ # how many seconds ara available for this task and user for the current day
61
+ def user_remaining_for_date(user, date)
62
+ total_seconds = user_total_seconds_for_date(user, date)
63
+ remaining = max_minutes_per_day * 60
64
+ remaining = remaining_seconds_for_today if date.beginning_of_day == Date.current
65
+
66
+ [remaining - total_seconds, 0].max
67
+ end
68
+
69
+ def user_remaining_for_today(user)
70
+ user_remaining_for_date(user, Date.current)
71
+ end
72
+
73
+ def assignation_pending?(user)
74
+ assignations.pending.where(user: user).count.positive?
75
+ end
76
+
77
+ def assignation_accepted?(user)
78
+ assignations.accepted.where(user: user).count.positive?
79
+ end
80
+
81
+ def assignation_rejected?(user)
82
+ assignations.rejected.where(user: user).count.positive?
83
+ end
84
+
85
+ def has_assignation?(user)
86
+ assignations.where(user: user).count.positive?
87
+ end
88
+
89
+ def has_questions?
90
+ questionnaire.questions.any?
91
+ end
92
+
93
+ def allow_answers_for?(user)
94
+ return false if status == :inactive
95
+
96
+ return false unless has_questions?
97
+
98
+ assignation_accepted?(user)
99
+ end
100
+
101
+ def answered_by?(user)
102
+ questionnaire.answered_by? session_token(user)
103
+ end
104
+
105
+ # used as a unique idenfier when answering the task associated questionnaire
106
+ def session_token(user)
107
+ "#{Digest::SHA1.hexdigest(user.id.to_s)}-#{id}"
108
+ end
109
+
110
+ # Returns a identificative (I18n) string about the current status of activity
111
+ # Returns:
112
+ # :open if user can track time
113
+ # :finished if current date is passed end date
114
+ # :not_started if current date has not reach start date
115
+ # :inactive if current status is inactive
116
+ def status
117
+ return :inactive unless active?
118
+ return :not_started if start_date > Time.current.beginning_of_day
119
+ return :finished if end_date < Time.current.beginning_of_day
120
+
121
+ :open
122
+ end
123
+
124
+ def self.log_presenter_class_for(_log)
125
+ Decidim::TimeTracker::AdminLog::ActivityPresenter
126
+ end
127
+
128
+ private
129
+
130
+ def last_event_for(user)
131
+ time_events.last_for(user)
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ # Abstract class from which all models in this engine inherit.
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ self.abstract_class = true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ # The data store for an assignation in the Decidim::TimeTracker component.
6
+ class Assignation < ApplicationRecord
7
+ include Decidim::Traceable
8
+ include Decidim::Loggable
9
+
10
+ self.table_name = :decidim_time_tracker_assignations
11
+
12
+ belongs_to :user,
13
+ foreign_key: "decidim_user_id",
14
+ class_name: "Decidim::User"
15
+
16
+ belongs_to :invited_by_user,
17
+ class_name: "Decidim::User",
18
+ optional: true
19
+
20
+ belongs_to :activity,
21
+ class_name: "Decidim::TimeTracker::Activity"
22
+
23
+ has_one :task,
24
+ class_name: "Decidim::TimeTracker::Task",
25
+ through: :activity
26
+
27
+ has_many :time_events,
28
+ class_name: "Decidim::TimeTracker::TimeEvent",
29
+ dependent: :nullify
30
+
31
+ has_many :milestones,
32
+ class_name: "Decidim::TimeTracker::Milestone",
33
+ through: :user
34
+
35
+ enum status: [:pending, :accepted, :rejected]
36
+
37
+ def assignee
38
+ Assignee.for(user)
39
+ end
40
+
41
+ def time_dedicated
42
+ time_events.sum(&:total_seconds)
43
+ end
44
+
45
+ def time_dedicated_to(activity)
46
+ time_events.where(activity: activity).sum(&:total_seconds)
47
+ end
48
+
49
+ def can_change_status?
50
+ time_events.empty?
51
+ end
52
+
53
+ # rubocop:disable Lint/UselessAssignment
54
+ def self.sorted_by_status(*statuses)
55
+ accepted = self.accepted.sort_by(&:time_dedicated).reverse
56
+ pending = self.pending
57
+ rejected = self.rejected
58
+
59
+ statuses.map { |status| send(status) }.sum
60
+ end
61
+ # rubocop:enable Lint/UselessAssignment
62
+
63
+ def self.log_presenter_class_for(_log)
64
+ Decidim::TimeTracker::AdminLog::AssignationPresenter
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ # The data store for an assignee in the Decidim::TimeTracker component.
6
+ class Assignee < ApplicationRecord
7
+ self.table_name = :decidim_time_tracker_assignees
8
+
9
+ belongs_to :user,
10
+ foreign_key: "decidim_user_id",
11
+ class_name: "Decidim::User"
12
+
13
+ has_many :tos_acceptances,
14
+ class_name: "Decidim::TimeTracker::TosAcceptance"
15
+
16
+ def self.for(user)
17
+ find_or_create_by(user: user)
18
+ end
19
+
20
+ def tos_accepted_at(time_tracker)
21
+ tos_acceptances.find_by(time_tracker: time_tracker)&.created_at
22
+ end
23
+
24
+ def tos_accepted?(time_tracker)
25
+ tos_acceptances.exists?(time_tracker: time_tracker)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ # The data store for a AssigneeData in the Decidim::TimeTracker component.
6
+ class AssigneeData < ApplicationRecord
7
+ include Decidim::Forms::HasQuestionnaire
8
+
9
+ self.table_name = :decidim_time_tracker_assignee_data
10
+
11
+ belongs_to :time_tracker,
12
+ class_name: "Decidim::TimeTracker::TimeTracker"
13
+
14
+ delegate :questions, :answers, to: :questionnaire
15
+
16
+ delegate :organization, :component, to: :time_tracker
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TimeTracker
5
+ # The data store for a Milestone in the Decidim::TimeTracker component.
6
+ class Milestone < ApplicationRecord
7
+ include Decidim::HasAttachments
8
+ include Decidim::Resourceable
9
+
10
+ self.table_name = :decidim_time_tracker_milestones
11
+
12
+ belongs_to :user,
13
+ foreign_key: "decidim_user_id",
14
+ class_name: "Decidim::User"
15
+
16
+ belongs_to :activity,
17
+ class_name: "Decidim::TimeTracker::Activity"
18
+
19
+ has_one :task,
20
+ through: :activity,
21
+ class_name: "Decidim::TimeTracker::Task"
22
+
23
+ delegate :component, to: :task
24
+ delegate :organization, to: :user
25
+ end
26
+ end
27
+ end