decidim-time_tracker 0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|