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.
- checksums.yaml +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +174 -0
- data/Rakefile +40 -0
- data/app/assets/config/admin/decidim_time_tracker_manifest.css +2 -0
- data/app/assets/config/admin/decidim_time_tracker_manifest.js +1 -0
- data/app/assets/config/decidim_time_tracker_manifest.css +3 -0
- data/app/assets/config/decidim_time_tracker_manifest.js +1 -0
- data/app/assets/images/decidim/time_tracker/icon.svg +1 -0
- data/app/assets/javascripts/decidim/time_tracker/activity_ui.js.es6 +112 -0
- data/app/assets/javascripts/decidim/time_tracker/admin/assignations.js.es6 +35 -0
- data/app/assets/javascripts/decidim/time_tracker/reports/reports.js.es6 +19 -0
- data/app/assets/javascripts/decidim/time_tracker/time_tracker.js.es6 +82 -0
- data/app/assets/javascripts/decidim/time_tracker/timer_api.js.es6 +55 -0
- data/app/assets/stylesheets/decidim/time_tracker/_variables.scss +1 -0
- data/app/assets/stylesheets/decidim/time_tracker/admin/time_tracker.scss +59 -0
- data/app/assets/stylesheets/decidim/time_tracker/time_tracker.scss +118 -0
- data/app/cells/decidim/time_tracker/milestone/show.erb +42 -0
- data/app/cells/decidim/time_tracker/milestone_cell.rb +54 -0
- data/app/commands/decidim/time_tracker/admin/create_activity.rb +39 -0
- data/app/commands/decidim/time_tracker/admin/create_assignation.rb +75 -0
- data/app/commands/decidim/time_tracker/admin/create_task.rb +33 -0
- data/app/commands/decidim/time_tracker/admin/create_time_tracker.rb +76 -0
- data/app/commands/decidim/time_tracker/admin/destroy_activity.rb +43 -0
- data/app/commands/decidim/time_tracker/admin/destroy_assignation.rb +43 -0
- data/app/commands/decidim/time_tracker/admin/destroy_task.rb +43 -0
- data/app/commands/decidim/time_tracker/admin/export_time_tracker.rb +150 -0
- data/app/commands/decidim/time_tracker/admin/update_activity.rb +49 -0
- data/app/commands/decidim/time_tracker/admin/update_assignation.rb +42 -0
- data/app/commands/decidim/time_tracker/admin/update_task.rb +44 -0
- data/app/commands/decidim/time_tracker/create_milestone.rb +57 -0
- data/app/commands/decidim/time_tracker/create_request_assignation.rb +39 -0
- data/app/commands/decidim/time_tracker/start_time_event.rb +104 -0
- data/app/commands/decidim/time_tracker/stop_last_time_event.rb +45 -0
- data/app/controllers/concerns/decidim/time_tracker/admin/filterable_assignations.rb +50 -0
- data/app/controllers/decidim/time_tracker/activity_questionnaire_controller.rb +76 -0
- data/app/controllers/decidim/time_tracker/admin/activities_controller.rb +82 -0
- data/app/controllers/decidim/time_tracker/admin/activity_questionnaire_controller.rb +52 -0
- data/app/controllers/decidim/time_tracker/admin/application_controller.rb +22 -0
- data/app/controllers/decidim/time_tracker/admin/assignations_controller.rb +84 -0
- data/app/controllers/decidim/time_tracker/admin/assignee_questionnaire_controller.rb +58 -0
- data/app/controllers/decidim/time_tracker/admin/stats_controller.rb +24 -0
- data/app/controllers/decidim/time_tracker/admin/tasks_controller.rb +112 -0
- data/app/controllers/decidim/time_tracker/admin/time_tracker_exports_controller.rb +25 -0
- data/app/controllers/decidim/time_tracker/application_controller.rb +24 -0
- data/app/controllers/decidim/time_tracker/assignations_controller.rb +38 -0
- data/app/controllers/decidim/time_tracker/assignee_questionnaire_controller.rb +80 -0
- data/app/controllers/decidim/time_tracker/milestones_controller.rb +62 -0
- data/app/controllers/decidim/time_tracker/reports/user_controller.rb +38 -0
- data/app/controllers/decidim/time_tracker/time_events_controller.rb +68 -0
- data/app/controllers/decidim/time_tracker/time_tracker_controller.rb +43 -0
- data/app/forms/decidim/time_tracker/admin/activity_form.rb +27 -0
- data/app/forms/decidim/time_tracker/admin/assignation_form.rb +29 -0
- data/app/forms/decidim/time_tracker/admin/task_form.rb +22 -0
- data/app/forms/decidim/time_tracker/milestone_form.rb +32 -0
- data/app/forms/decidim/time_tracker/time_event_form.rb +47 -0
- data/app/helpers/decidim/time_tracker/admin/application_helper.rb +33 -0
- data/app/helpers/decidim/time_tracker/application_helper.rb +95 -0
- data/app/jobs/decidim/time_tracker/stop_counter_job.rb +29 -0
- data/app/models/decidim/time_tracker/activity.rb +135 -0
- data/app/models/decidim/time_tracker/application_record.rb +10 -0
- data/app/models/decidim/time_tracker/assignation.rb +68 -0
- data/app/models/decidim/time_tracker/assignee.rb +29 -0
- data/app/models/decidim/time_tracker/assignee_data.rb +19 -0
- data/app/models/decidim/time_tracker/milestone.rb +27 -0
- data/app/models/decidim/time_tracker/task.rb +44 -0
- data/app/models/decidim/time_tracker/time_event.rb +62 -0
- data/app/models/decidim/time_tracker/time_tracker.rb +46 -0
- data/app/models/decidim/time_tracker/tos_acceptance.rb +16 -0
- data/app/permissions/decidim/time_tracker/admin/permissions.rb +69 -0
- data/app/permissions/decidim/time_tracker/permissions.rb +69 -0
- data/app/presenters/decidim/time_tracker/admin_log/activity_presenter.rb +44 -0
- data/app/presenters/decidim/time_tracker/admin_log/assignation_presenter.rb +48 -0
- data/app/presenters/decidim/time_tracker/admin_log/task_presenter.rb +46 -0
- data/app/presenters/decidim/time_tracker/admin_log/value_types/activity_presenter.rb +28 -0
- data/app/presenters/decidim/time_tracker/admin_log/value_types/time_tracker_presenter.rb +28 -0
- data/app/views/decidim/time_tracker/admin/_breadcrumbs.html.erb +56 -0
- data/app/views/decidim/time_tracker/admin/activities/_form.html.erb +41 -0
- data/app/views/decidim/time_tracker/admin/activities/_index.html.erb +73 -0
- data/app/views/decidim/time_tracker/admin/activities/edit.html.erb +5 -0
- data/app/views/decidim/time_tracker/admin/activities/new.html.erb +5 -0
- data/app/views/decidim/time_tracker/admin/assignations/_form.html.erb +30 -0
- data/app/views/decidim/time_tracker/admin/assignations/index.html.erb +67 -0
- data/app/views/decidim/time_tracker/admin/assignations/new.html.erb +9 -0
- data/app/views/decidim/time_tracker/admin/questionnaires/_index.html.erb +27 -0
- data/app/views/decidim/time_tracker/admin/questionnaires/_questionnaire.html.erb +21 -0
- data/app/views/decidim/time_tracker/admin/stats/index.html.erb +50 -0
- data/app/views/decidim/time_tracker/admin/tasks/_assignations.html.erb +60 -0
- data/app/views/decidim/time_tracker/admin/tasks/_form.html.erb +7 -0
- data/app/views/decidim/time_tracker/admin/tasks/_index.html.erb +61 -0
- data/app/views/decidim/time_tracker/admin/tasks/edit.html.erb +16 -0
- data/app/views/decidim/time_tracker/admin/tasks/index.html.erb +5 -0
- data/app/views/decidim/time_tracker/admin/tasks/new.html.erb +12 -0
- data/app/views/decidim/time_tracker/milestones/index.html.erb +60 -0
- data/app/views/decidim/time_tracker/reports/user/index.html.erb +41 -0
- data/app/views/decidim/time_tracker/time_tracker/_activity.html.erb +43 -0
- data/app/views/decidim/time_tracker/time_tracker/_answer_questionnaire.html.erb +7 -0
- data/app/views/decidim/time_tracker/time_tracker/_assignee_data.html.erb +9 -0
- data/app/views/decidim/time_tracker/time_tracker/_callout_status.html.erb +3 -0
- data/app/views/decidim/time_tracker/time_tracker/_idle_activity.html.erb +27 -0
- data/app/views/decidim/time_tracker/time_tracker/_milestone_form.html.erb +35 -0
- data/app/views/decidim/time_tracker/time_tracker/_request_activity.html.erb +22 -0
- data/app/views/decidim/time_tracker/time_tracker/_sign_in.html.erb +5 -0
- data/app/views/decidim/time_tracker/time_tracker/index.html.erb +59 -0
- data/config/activity_questionnaire.yml +26 -0
- data/config/assignee_questionnaire.yml +37 -0
- data/config/i18n-tasks.yml +10 -0
- data/config/locales/ca.yml +394 -0
- data/config/locales/cs.yml +394 -0
- data/config/locales/en.yml +419 -0
- data/config/locales/es.yml +394 -0
- data/db/migrate/20200302115642_create_decidim_time_tracker_time_trackers.rb +10 -0
- data/db/migrate/20200302115654_create_decidim_time_tracker_tasks.rb +11 -0
- data/db/migrate/20200302115658_create_decidim_time_tracker_assignees.rb +12 -0
- data/db/migrate/20200302115700_create_decidim_time_tracker_activities.rb +19 -0
- data/db/migrate/20200302115708_create_decidim_time_tracker_milestones.rb +13 -0
- data/db/migrate/20200302115831_create_decidim_time_tracker_assignations.rb +16 -0
- data/db/migrate/20200302115840_create_decidim_time_tracker_time_events.rb +15 -0
- data/db/migrate/20201118153327_create_decidim_time_tracker_assignee_data.rb +10 -0
- data/db/migrate/20201223115130_create_decidim_time_tracker_tos_acceptances.rb +18 -0
- data/lib/decidim/time_tracker.rb +45 -0
- data/lib/decidim/time_tracker/admin.rb +10 -0
- data/lib/decidim/time_tracker/admin_engine.rb +51 -0
- data/lib/decidim/time_tracker/component.rb +248 -0
- data/lib/decidim/time_tracker/engine.rb +59 -0
- data/lib/decidim/time_tracker/reports.rb +10 -0
- data/lib/decidim/time_tracker/reports_engine.rb +34 -0
- data/lib/decidim/time_tracker/test/admin_log_presenter_examples.rb +34 -0
- data/lib/decidim/time_tracker/test/factories.rb +110 -0
- data/lib/decidim/time_tracker/test/time_tracker_context.rb +18 -0
- data/lib/decidim/time_tracker/time_tracker_activity_questionnaire_answers_serializer.rb +74 -0
- data/lib/decidim/time_tracker/version.rb +9 -0
- data/vendor/assets/javascripts/jsrender.min.js +4 -0
- metadata +276 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
class AssigneeQuestionnaireController < Decidim::TimeTracker::ApplicationController
|
|
6
|
+
include Decidim::Forms::Concerns::HasQuestionnaire
|
|
7
|
+
|
|
8
|
+
# only allows answers if not in preview mode
|
|
9
|
+
def preview
|
|
10
|
+
return show if request.method == "GET"
|
|
11
|
+
|
|
12
|
+
flash[":alert"] = I18n.t("questionnaire_in_preview_mode", scope: "decidim.time_tracker.time_tracker")
|
|
13
|
+
redirect_to preview_assignee_questionnaire_path
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# overwrite this method to automatically accept TOS
|
|
17
|
+
def answer
|
|
18
|
+
enforce_permission_to :answer, :questionnaire
|
|
19
|
+
|
|
20
|
+
@form = form(Decidim::Forms::QuestionnaireForm).from_params(params, session_token: session_token, ip_hash: ip_hash)
|
|
21
|
+
|
|
22
|
+
Decidim::Forms::AnswerQuestionnaire.call(@form, current_user, questionnaire) do
|
|
23
|
+
on(:ok) do
|
|
24
|
+
# i18n-tasks-use t("decidim.forms.questionnaires.answer.success")
|
|
25
|
+
TosAcceptance.create!(assignee: Assignee.for(current_user), time_tracker: time_tracker)
|
|
26
|
+
flash[:notice] = I18n.t("answer.success", scope: i18n_flashes_scope)
|
|
27
|
+
redirect_to after_answer_path
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
on(:invalid) do
|
|
31
|
+
# i18n-tasks-use t("decidim.forms.questionnaires.answer.invalid")
|
|
32
|
+
flash.now[:alert] = I18n.t("answer.invalid", scope: i18n_flashes_scope)
|
|
33
|
+
render template: "decidim/forms/questionnaires/show"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def questionnaire_for
|
|
39
|
+
time_tracker.assignee_data
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# only allows answers if not in preview mode
|
|
43
|
+
def allow_answers?
|
|
44
|
+
return false if current_user.blank?
|
|
45
|
+
|
|
46
|
+
return true if params[:action] == "preview" && current_user.admin?
|
|
47
|
+
|
|
48
|
+
current_component.published?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Override so can answer only if is an assignation can view
|
|
52
|
+
# Also admins can preview it (but not answer)
|
|
53
|
+
def visitor_can_answer?
|
|
54
|
+
current_user.present?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def visitor_already_answered?
|
|
58
|
+
return false if params[:action] == "preview" && current_user.admin?
|
|
59
|
+
|
|
60
|
+
questionnaire.answered_by?(current_user)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns the path to answer this questionnaire for normal users
|
|
64
|
+
# If the questionnaire is rendered in a preview route, then just do nothing with the responses
|
|
65
|
+
def update_url
|
|
66
|
+
return preview_assignee_questionnaire_path if params[:action] == "preview"
|
|
67
|
+
|
|
68
|
+
answer_assignee_questionnaire_path
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def form_path
|
|
72
|
+
assignee_questionnaire_path
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def after_answer_path
|
|
76
|
+
Decidim::EngineRouter.main_proxy(current_component).root_path
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
class MilestonesController < Decidim::TimeTracker::ApplicationController
|
|
6
|
+
include Decidim::FormFactory
|
|
7
|
+
include Decidim::TimeTracker::ApplicationHelper
|
|
8
|
+
|
|
9
|
+
helper_method :user, :activities
|
|
10
|
+
|
|
11
|
+
def index
|
|
12
|
+
return redirect_to root_path if user.blank?
|
|
13
|
+
|
|
14
|
+
enforce_permission_to :index, :milestones, user: user
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# creates a milestone
|
|
18
|
+
def create
|
|
19
|
+
enforce_permission_to :create, :milestone, activity: activity
|
|
20
|
+
|
|
21
|
+
@form = form(MilestoneForm).from_params(milestone_params)
|
|
22
|
+
|
|
23
|
+
CreateMilestone.call(@form, current_user) do
|
|
24
|
+
on(:ok) do |_milestone|
|
|
25
|
+
flash[:notice] = I18n.t("milestones.create.success", scope: "decidim.time_tracker")
|
|
26
|
+
redirect_to milestones_path(nickname: current_user.nickname)
|
|
27
|
+
end
|
|
28
|
+
on(:invalid) do |_message|
|
|
29
|
+
flash[:alert] = I18n.t("milestones.create.error", scope: "decidim.time_tracker")
|
|
30
|
+
redirect_to root_path
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# copies the title to the attachment
|
|
38
|
+
def milestone_params
|
|
39
|
+
unsafe = params.to_unsafe_h
|
|
40
|
+
unsafe["milestone"]["attachment"]["title"] = { I18n.locale => params[:milestone][:title] } if unsafe["milestone"]["attachment"].present?
|
|
41
|
+
unsafe
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def root_path
|
|
45
|
+
Decidim::EngineRouter.main_proxy(current_component).root_path
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def activity
|
|
49
|
+
@activity ||= Activity.active.find_by(id: params[:milestone][:activity_id])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def activities
|
|
53
|
+
@activities ||= Activity.select("DISTINCT ON (id) *")
|
|
54
|
+
.where(id: Milestone.where(user: user).pluck(:activity_id))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def user
|
|
58
|
+
@user ||= Decidim::User.find_by(nickname: params[:nickname])
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
module Reports
|
|
6
|
+
# The controller to handle the user's time_tracker report page.
|
|
7
|
+
class UserController < Decidim::ApplicationController
|
|
8
|
+
include Decidim::ComponentPathHelper
|
|
9
|
+
include Decidim::UserProfile
|
|
10
|
+
|
|
11
|
+
helper Decidim::TimeTracker::ApplicationHelper
|
|
12
|
+
helper_method :activities, :assignations, :total_time, :activity_path
|
|
13
|
+
|
|
14
|
+
def index
|
|
15
|
+
enforce_permission_to :read, :user, current_user: current_user
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def activities
|
|
21
|
+
Decidim::TimeTracker::Activity.joins(:assignations).where("decidim_time_tracker_assignations.decidim_user_id": current_user.id).group_by(&:id)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def assignations
|
|
25
|
+
Assignation.where(user: current_user).sorted_by_status(:accepted, :pending, :rejected)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def activity_path(assignation)
|
|
29
|
+
EngineRouter.main_proxy(assignation.task.time_tracker.component).root_path(locale: params[:locale], activity: assignation.activity)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def total_time
|
|
33
|
+
assignations.map(&:time_dedicated).sum
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
class TimeEventsController < Decidim::TimeTracker::ApplicationController
|
|
6
|
+
include Decidim::FormFactory
|
|
7
|
+
|
|
8
|
+
helper_method :activity, :task, :assignation
|
|
9
|
+
|
|
10
|
+
def start
|
|
11
|
+
enforce_permission_to :start, :time_events
|
|
12
|
+
|
|
13
|
+
form = form(TimeEventForm).from_params(activity: activity, assignation: assignation)
|
|
14
|
+
StartTimeEvent.call(form) do
|
|
15
|
+
on(:ok) do |time_event|
|
|
16
|
+
render json: { message: I18n.t("time_events.start.success", scope: "decidim.time_tracker"),
|
|
17
|
+
id: time_event.id,
|
|
18
|
+
start: time_event.start,
|
|
19
|
+
startTime: time_event.start_time.iso8601,
|
|
20
|
+
elapsedSeconds: activity.user_total_seconds(current_user) }
|
|
21
|
+
end
|
|
22
|
+
on(:already_active) do |time_event|
|
|
23
|
+
render json: { message: I18n.t("time_events.start.already_started", scope: "decidim.time_tracker"),
|
|
24
|
+
error: I18n.t("time_events.start.already_started", scope: "decidim.time_tracker"),
|
|
25
|
+
id: time_event.id }
|
|
26
|
+
end
|
|
27
|
+
on(:invalid) do |message|
|
|
28
|
+
render json: { message: I18n.t("time_events.start.error", scope: "decidim.time_tracker"),
|
|
29
|
+
error: message }, status: :unprocessable_entity
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def stop
|
|
35
|
+
enforce_permission_to :stop, :time_events
|
|
36
|
+
|
|
37
|
+
StopLastTimeEvent.call(current_user) do
|
|
38
|
+
on(:ok) do |time_event|
|
|
39
|
+
render json: { message: I18n.t("time_events.stop.success", scope: "decidim.time_tracker"),
|
|
40
|
+
timeEvent: time_event.to_json }
|
|
41
|
+
end
|
|
42
|
+
on(:already_stopped) do |_time_event|
|
|
43
|
+
render json: { message: I18n.t("time_events.stop.error", scope: "decidim.time_tracker"),
|
|
44
|
+
error: I18n.t("time_events.stop.already_stopped", scope: "decidim.time_tracker") }
|
|
45
|
+
end
|
|
46
|
+
on(:invalid) do |message|
|
|
47
|
+
render json: { message: I18n.t("time_events.stop.error", scope: "decidim.time_tracker"),
|
|
48
|
+
error: message }, status: :unprocessable_entity
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def task
|
|
56
|
+
Task.find(params[:task_id])
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def activity
|
|
60
|
+
Activity.find(params[:activity_id])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def assignation
|
|
64
|
+
Assignation.find_by(user: current_user, activity: activity)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
class TimeTrackerController < Decidim::TimeTracker::ApplicationController
|
|
6
|
+
include Decidim::FormFactory
|
|
7
|
+
|
|
8
|
+
helper_method :tasks, :assignation_milestones, :start_endpoint, :stop_endpoint, :requests_path, :questionnaire_path
|
|
9
|
+
|
|
10
|
+
def index
|
|
11
|
+
@form = form(MilestoneForm).from_params(
|
|
12
|
+
attachment: form(AttachmentForm).instance
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def tasks
|
|
19
|
+
time_tracker.tasks
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def assignation_milestones(activity)
|
|
23
|
+
Milestone.where(activity: activity).order(created_at: :desc).select("DISTINCT ON (decidim_user_id, created_at) *")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def start_endpoint(activity)
|
|
27
|
+
Decidim::EngineRouter.main_proxy(current_component).task_activity_start_path(activity.task, activity.id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def stop_endpoint(activity)
|
|
31
|
+
Decidim::EngineRouter.main_proxy(current_component).task_activity_stop_path(activity.task, activity.id)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def requests_path(activity)
|
|
35
|
+
Decidim::EngineRouter.main_proxy(current_component).assignations_path(activity_id: activity.id)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def questionnaire_path(activity)
|
|
39
|
+
Decidim::EngineRouter.main_proxy(current_component).new_assignation_path(task_id: activity.task, activity_id: activity.id, id: activity.questionnaire.id)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
module Admin
|
|
6
|
+
# This class holds a Form to create/update activity from Decidim's admin panel
|
|
7
|
+
class ActivityForm < Decidim::Form
|
|
8
|
+
include TranslatableAttributes
|
|
9
|
+
|
|
10
|
+
translatable_attribute :description, String
|
|
11
|
+
|
|
12
|
+
attribute :active, Boolean
|
|
13
|
+
attribute :start_date, Decidim::Attributes::TimeWithZone
|
|
14
|
+
attribute :end_date, Decidim::Attributes::TimeWithZone
|
|
15
|
+
attribute :max_minutes_per_day, Integer
|
|
16
|
+
attribute :requests_start_at, Decidim::Attributes::TimeWithZone
|
|
17
|
+
|
|
18
|
+
validates :start_date, presence: true
|
|
19
|
+
validates :end_date, presence: true, date: { after: :start_date }
|
|
20
|
+
validates :requests_start_at, presence: true, date: { before: :start_date }
|
|
21
|
+
|
|
22
|
+
validates :max_minutes_per_day, presence: true
|
|
23
|
+
validates :description, translatable_presence: true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
module Admin
|
|
6
|
+
class AssignationForm < Decidim::Form
|
|
7
|
+
mimic :assignation
|
|
8
|
+
|
|
9
|
+
attribute :user_id, Integer
|
|
10
|
+
attribute :existing_user, Boolean, default: false
|
|
11
|
+
attribute :name, String
|
|
12
|
+
attribute :email, String
|
|
13
|
+
|
|
14
|
+
validates :name, presence: true, unless: proc { |object| object.existing_user }
|
|
15
|
+
validates :email, presence: true, unless: proc { |object| object.existing_user }
|
|
16
|
+
validates :user, presence: true, if: proc { |object| object.existing_user }
|
|
17
|
+
|
|
18
|
+
def map_model(model)
|
|
19
|
+
self.user_id = model.decidim_user_id
|
|
20
|
+
self.existing_user = user_id.present?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def user
|
|
24
|
+
@user ||= current_organization.users.find_by(id: user_id)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
module Admin
|
|
6
|
+
# This class holds a Form to create/update task from Decidim's admin panel.
|
|
7
|
+
class TaskForm < Decidim::Form
|
|
8
|
+
include TranslatableAttributes
|
|
9
|
+
|
|
10
|
+
mimic :task
|
|
11
|
+
|
|
12
|
+
translatable_attribute :name, String
|
|
13
|
+
|
|
14
|
+
validates :name, translatable_presence: true
|
|
15
|
+
|
|
16
|
+
def time_tracker
|
|
17
|
+
@time_tracker ||= Decidim::TimeTracker::TimeTracker.find_by(component: current_component)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
class MilestoneForm < Decidim::Form
|
|
6
|
+
mimic :milestone
|
|
7
|
+
|
|
8
|
+
attribute :activity_id, Integer
|
|
9
|
+
attribute :title, String
|
|
10
|
+
attribute :description, String
|
|
11
|
+
attribute :attachment, AttachmentForm
|
|
12
|
+
|
|
13
|
+
validates :activity_id, :title, presence: true
|
|
14
|
+
validates :activity, presence: true, if: ->(form) { form.activity_id.present? }
|
|
15
|
+
validate :notify_missing_attachment_if_errored
|
|
16
|
+
|
|
17
|
+
def activity
|
|
18
|
+
@activity ||= Activity.find_by(id: activity_id)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# This method will add an error to the `attachment` field only if there's
|
|
24
|
+
# any error in any other field. This is needed because when the form has
|
|
25
|
+
# an error, the attachment is lost, so we need a way to inform the user of
|
|
26
|
+
# this problem.
|
|
27
|
+
def notify_missing_attachment_if_errored
|
|
28
|
+
errors.add(:attachment, :needs_to_be_reattached) if errors.any? && attachment.present?
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
class TimeEventForm < Decidim::Form
|
|
6
|
+
mimic :time_event
|
|
7
|
+
|
|
8
|
+
attribute :activity, Decidim::TimeTracker::Activity
|
|
9
|
+
attribute :assignation, Decidim::TimeTracker::Assignation
|
|
10
|
+
attribute :user_id, Integer
|
|
11
|
+
attribute :start, Integer
|
|
12
|
+
attribute :stop, Integer
|
|
13
|
+
|
|
14
|
+
validates :activity, presence: true
|
|
15
|
+
validates :assignation, presence: true
|
|
16
|
+
|
|
17
|
+
validate :assigned_to_activity?
|
|
18
|
+
validate :activity_is_active
|
|
19
|
+
validate :dates_are_valid
|
|
20
|
+
validates :stop, date: { after: ->(form) { form.start } }, if: ->(form) { form.stop.present? && form.start.present? }
|
|
21
|
+
|
|
22
|
+
def user
|
|
23
|
+
return Decidim::User.find(user_id) if user_id.present?
|
|
24
|
+
|
|
25
|
+
assignation.user
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def assigned_to_activity?
|
|
31
|
+
errors.add(:assignation, :unassigned) unless activity.assignations.accepted.find_by(id: assignation&.id)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def activity_is_active
|
|
35
|
+
errors.add(:activity, :inactive) unless activity.active?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def dates_are_valid
|
|
39
|
+
[:start, :stop].each do |key|
|
|
40
|
+
next unless attributes[key]
|
|
41
|
+
|
|
42
|
+
errors.add(key, :date_format) unless attributes[key].is_a?(Time)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|