diff --git a/Gemfile b/Gemfile index 4b30193..5edcd45 100644 --- a/Gemfile +++ b/Gemfile @@ -30,9 +30,9 @@ gem "active_storage_base64", "~> 1.0.0" gem "image_processing", "~> 1.2" # Use Amazon Web Services S3 for file uploads in production -gem "aws-sdk-s3", "~> 1.31.0", require: false, group: [:production, :review] +gem "aws-sdk-s3", "~> 1.48", require: false, group: [:production, :review] # Allow AWS API requests to be signed using IAM credentials -gem "aws-sigv4", "~> 1.0.2" +gem "aws-sigv4", "~> 1.1" # Reduces boot times through caching; required in config/boot.rb gem "bootsnap", ">= 1.1.0", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 25f6e5e..6998497 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -117,21 +117,22 @@ GEM ast (2.4.0) autoprefixer-rails (9.7.3) execjs - aws-eventstream (1.0.3) - aws-partitions (1.210.0) - aws-sdk-core (3.46.2) - aws-eventstream (~> 1.0) - aws-partitions (~> 1.0) - aws-sigv4 (~> 1.0) + aws-eventstream (1.1.0) + aws-partitions (1.337.0) + aws-sdk-core (3.102.1) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.239.0) + aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.13.0) - aws-sdk-core (~> 3, >= 3.39.0) - aws-sigv4 (~> 1.0) - aws-sdk-s3 (1.31.0) - aws-sdk-core (~> 3, >= 3.39.0) + aws-sdk-kms (1.35.0) + aws-sdk-core (~> 3, >= 3.99.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.72.0) + aws-sdk-core (~> 3, >= 3.102.1) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.0) - aws-sigv4 (1.0.3) + aws-sigv4 (~> 1.1) + aws-sigv4 (1.2.1) + aws-eventstream (~> 1, >= 1.0.2) axlsx (3.0.0.pre) htmlentities (~> 4.3, >= 4.3.4) mimemagic (~> 0.3) @@ -514,8 +515,8 @@ DEPENDENCIES activeresource (= 5.1.0) acts-as-taggable-on! analytics-ruby - aws-sdk-s3 (~> 1.31.0) - aws-sigv4 (~> 1.0.2) + aws-sdk-s3 (~> 1.48) + aws-sigv4 (~> 1.1) axlsx (~> 3.0.0.pre) axlsx_rails (~> 0.5.2) axlsx_styler (~> 0.2.0) diff --git a/app/assets/stylesheets/_bootstrap_overrides.scss b/app/assets/stylesheets/_bootstrap_overrides.scss index d71920a..1948109 100644 --- a/app/assets/stylesheets/_bootstrap_overrides.scss +++ b/app/assets/stylesheets/_bootstrap_overrides.scss @@ -14,6 +14,7 @@ $red: #F9002B; $green: #51B61B; $teal: #32C498; $purple: #5139EE; +$yellow: #F9BE1B; $dark: $gray-900; $success: $teal; $link-color: $body-color; diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 7f74913..b14620f 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -37,14 +37,14 @@ label { &.release-me { span:last-child { background-color: $teal; - color: $body-color; + color: $white; } } &.direct-me { span:last-child { background-color: $green; - color: $body-color; + color: $white; } } @@ -58,7 +58,14 @@ label { &.deliver-me { span:last-child { background-color: $purple; - color: white; + color: $white; + } + } + + &.task-me { + span:last-child { + background-color: $yellow; + color: $white; } } diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 8ab0d1d..a5e17b5 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -17,7 +17,7 @@ class AccountsController < ApplicationController if sign_in(user) TrackAnalyticsJob.perform_later(user, user.primary_account, :track_guest_sign_up, user_agent: request.user_agent, user_ip: request.remote_ip) - SubmitHubspotFormJob.perform_later(user.email, account.name, i_m_interested_in: user.interested_product_name) + SubmitHubspotFormJob.perform_later(user.first_name, user.last_name, user.email, account.name, i_m_interested_in: user.interested_product_name) redirect_to signed_in_root_path else redirect_to new_session_path, alert: t(".notice") diff --git a/app/controllers/admin/task_requests_controller.rb b/app/controllers/admin/task_requests_controller.rb new file mode 100644 index 0000000..59261ff --- /dev/null +++ b/app/controllers/admin/task_requests_controller.rb @@ -0,0 +1,36 @@ +class Admin::TaskRequestsController < Admin::ApplicationController + before_action :set_task_request, only: [:edit, :update, :show] + + def index + @task_requests = task_requests.order_by_recent.paginate(page: params[:page]) + end + + def edit + end + + def show + @files = @task_request.files.paginate(page: params[:page]) + end + + def update + if @task_request.update(task_request_params) + redirect_to [:admin, :task_requests], notice: t(".notice") + else + render :edit + end + end + + private + + def task_request_params + params.require(:task_request).permit(:status, :deliverable_url) + end + + def task_requests + policy_scope TaskRequest + end + + def set_task_request + @task_request = authorize policy_scope(TaskRequest).find(params[:id]) + end +end diff --git a/app/controllers/api/appearance_releases_controller.rb b/app/controllers/api/appearance_releases_controller.rb index 1744231..6057030 100644 --- a/app/controllers/api/appearance_releases_controller.rb +++ b/app/controllers/api/appearance_releases_controller.rb @@ -19,5 +19,11 @@ class Api::AppearanceReleasesController < Api::ReleasesController guardian_photo[:io] = StringIO.new(Base64.decode64(guardian_photo[:io])) release.guardian_photo.attach(io: guardian_photo[:io], filename: guardian_photo[:filename]) end + + guardian_2_photo = release_create_params[:guardian_2_photo] + if guardian_2_photo + guardian_2_photo[:io] = StringIO.new(Base64.decode64(guardian_2_photo[:io])) + release.guardian_2_photo.attach(io: guardian_2_photo[:io], filename: guardian_2_photo[:filename]) + end end end diff --git a/app/controllers/api/releases_controller.rb b/app/controllers/api/releases_controller.rb index bf6540a..29e2ecb 100644 --- a/app/controllers/api/releases_controller.rb +++ b/app/controllers/api/releases_controller.rb @@ -103,10 +103,16 @@ class Api::ReleasesController < Api::ApiController if ["appearance_release", "talent_release"].include?(name) has_many :guardian_photos do data do - [@object.guardian_photo.try(:attachment)].compact + [ + @object.guardian_photo.try(:attachment), + @object.guardian_2_photo.try(:attachment) + ].compact end meta do - { count: @object.try(:guardian_photo).try(:attached?) ? 1 : 0 } + { count: + (@object.try(:guardian_photo).try(:attached?) ? 1 : 0) + + (@object.try(:guardian_2_photo).try(:attached?) ? 1 : 0) + } end end end @@ -170,7 +176,7 @@ class Api::ReleasesController < Api::ApiController def release_create_params parameters = params.require(model_name).permit! - keys = model_constant.new.attributes.keys + [:guardian_photo, :person_photo, :photos, :signature, :signature_base64, :file_infos_attributes] + keys = model_constant.new.attributes.keys + [:guardian_photo, :guardian_2_photo, :person_photo, :photos, :signature, :signature_base64, :file_infos_attributes] parameters[:signature_base64] = parameters[:signature] parameters = parameters.slice(*keys).except(:created_at, :updated_at, :id, :user_id, :signature) parameters diff --git a/app/controllers/api/talent_releases_controller.rb b/app/controllers/api/talent_releases_controller.rb index 6630942..d632a9f 100644 --- a/app/controllers/api/talent_releases_controller.rb +++ b/app/controllers/api/talent_releases_controller.rb @@ -17,5 +17,11 @@ class Api::TalentReleasesController < Api::ReleasesController guardian_photo[:io] = StringIO.new(Base64.decode64(guardian_photo[:io])) release.guardian_photo.attach(io: guardian_photo[:io], filename: guardian_photo[:filename]) end + + guardian_2_photo = release_create_params[:guardian_2_photo] + if guardian_2_photo + guardian_2_photo[:io] = StringIO.new(Base64.decode64(guardian_2_photo[:io])) + release.guardian_2_photo.attach(io: guardian_2_photo[:io], filename: guardian_2_photo[:filename]) + end end end diff --git a/app/controllers/stream_notifications_controller.rb b/app/controllers/stream_notifications_controller.rb index 11cf20d..12b8ee2 100644 --- a/app/controllers/stream_notifications_controller.rb +++ b/app/controllers/stream_notifications_controller.rb @@ -60,9 +60,14 @@ class StreamNotificationsController < ApplicationController def set_broadcast if notification_type == "video.asset.static_renditions.ready" live_stream_id = notification.dig(:stream_notification, :data, :live_stream_id) - @broadcast = Broadcast.find_by!(stream_uid: live_stream_id) + @broadcast = Broadcast.find_by(stream_uid: live_stream_id) else - @broadcast = Broadcast.find_by!(stream_uid: notification_object_id) + @broadcast = Broadcast.find_by(stream_uid: notification_object_id) + end + + if @broadcast.nil? + logger.info "Ignoring broadcast from other environment. Type = #{notification_type}. Id = #{live_stream_id} / #{notification_object_id}" + head :ok end end diff --git a/app/controllers/task_requests_controller.rb b/app/controllers/task_requests_controller.rb new file mode 100644 index 0000000..07172d0 --- /dev/null +++ b/app/controllers/task_requests_controller.rb @@ -0,0 +1,76 @@ +class TaskRequestsController < ApplicationController + layout "project" + + before_action :set_project + before_action :build_task_request, only: [:new, :create] + before_action :set_task_request, only: [:show, :edit, :update, :cancel] + + def index + @task_requests = task_requests.order_by_recent.paginate(page: params[:page]) + end + + def new + end + + def create + @task_request.attributes = task_request_params_with_email + + if @task_request.save + log_create_analytics + taskme_url = url_for([:admin, @task_request]) + SubmitHubspotTaskRequestFormJob.perform_later(@task_request.user_email, taskme_url) + else + render :new + end + end + + def show + @files = @task_request.files.paginate(page: params[:page]) + end + + def edit + end + + def update + if @task_request.update(task_request_params) + redirect_to [@project, :task_requests], notice: t(".notice") + else + render :edit + end + end + + def cancel + @task_request.cancelled! + redirect_to [@project, :task_requests], notice: t(".notice") + end + + private + + def task_request_params + params.require(:task_request).permit(:description, :deadline, :time_allowed, :additional_notes, files: []) + end + + def task_request_params_with_email + task_request_params.merge(user_email: Current.user.email) + end + + def set_project + @project = policy_scope(Project).find(params[:project_id]) + end + + def set_task_request + @task_request = authorize policy_scope(TaskRequest).find(params[:id]) + end + + def task_requests + authorize policy_scope(@project.task_requests) + end + + def build_task_request + @task_request = authorize @project.task_requests.build + end + + def log_create_analytics + TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_task_request, user_agent: request.user_agent, user_ip: request.remote_ip) + end +end diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb new file mode 100644 index 0000000..18c52dd --- /dev/null +++ b/app/controllers/tasks_controller.rb @@ -0,0 +1,19 @@ +class TasksController < ApplicationController + before_action :set_project + + include ProjectLayout + + def index + @tasks = task_requests.completed.order_by_recent.paginate(page: params[:page]) + end + + private + + def set_project + @project = policy_scope(Project).find(params[:project_id]) + end + + def task_requests + authorize policy_scope(@project.task_requests) + end +end diff --git a/app/helpers/description_list_helper.rb b/app/helpers/description_list_helper.rb index 23f94e9..e6f8c66 100644 --- a/app/helpers/description_list_helper.rb +++ b/app/helpers/description_list_helper.rb @@ -7,8 +7,8 @@ module DescriptionListHelper safe_join(tags) end - def description_list_pair_for(record, attribute, append: nil) - term = translation_for(record, attribute) + def description_list_pair_for(record, attribute, append: nil, custom_label: nil) + term = custom_label.nil? ? translation_for(record, attribute) : custom_label definition = record.send(attribute) description_list_pair(term, definition, append: append) diff --git a/app/helpers/dropzone_helper.rb b/app/helpers/dropzone_helper.rb index 1b03afc..321bac3 100644 --- a/app/helpers/dropzone_helper.rb +++ b/app/helpers/dropzone_helper.rb @@ -11,6 +11,8 @@ module DropzoneHelper t 'location_releases.form.photos.dropzone_label' when "directory" "To Add Files to the Folder:
Drag & Drop Files
or
Click or Tap here to browse files" + when "task_request" + "To Add Files for the Task:
Drag & Drop Files
or
Click or Tap here to browse files" else "To Add Photos to the release:
Drag & Drop Files
or
Click or Tap here to browse photos and connect to Camera" end diff --git a/app/javascript/packs/datepickers.js b/app/javascript/packs/datepickers.js index f0e1029..2451d1d 100644 --- a/app/javascript/packs/datepickers.js +++ b/app/javascript/packs/datepickers.js @@ -1,5 +1,6 @@ $(document).on("turbolinks:load", () => { $('.datepicker-control').datepicker({ - format: "yyyy-mm-dd" + format: "yyyy-mm-dd", + todayHighlight: true }); }); diff --git a/app/jobs/submit_hubspot_form_job.rb b/app/jobs/submit_hubspot_form_job.rb index d83ba19..2bdcd43 100644 --- a/app/jobs/submit_hubspot_form_job.rb +++ b/app/jobs/submit_hubspot_form_job.rb @@ -1,12 +1,14 @@ class SubmitHubspotFormJob < ApplicationJob queue_as :default - def perform(email, company_name, additional_params = {}) + def perform(first_name, last_name, email, company_name, additional_params = {}) hubspot_form_guid = ENV["HUBSPOT_FORM_GUID"] return unless hubspot_form_guid.present? submission_params = { - email: email, + first_name: first_name, + last_name: last_name, + email: email, company: company_name }.merge(additional_params) diff --git a/app/jobs/submit_hubspot_task_request_form_job.rb b/app/jobs/submit_hubspot_task_request_form_job.rb new file mode 100644 index 0000000..11877cf --- /dev/null +++ b/app/jobs/submit_hubspot_task_request_form_job.rb @@ -0,0 +1,18 @@ +class SubmitHubspotTaskRequestFormJob < ApplicationJob + queue_as :default + + def perform(user_email, taskme_url) + hubspot_task_request_form_guid = ENV["HUBSPOT_TASK_REQUEST_FORM_GUID"] + return unless hubspot_task_request_form_guid.present? + + submission_params = { + email: user_email, + taskme_url: taskme_url + } + + form = Hubspot::Form.new("guid" => hubspot_task_request_form_guid) + is_form_sumitted = form.submit(submission_params) + + raise StandardError.new "Failed to submit the task request hubspot form data: #{is_form_sumitted}" unless is_form_sumitted + end +end diff --git a/app/models/account.rb b/app/models/account.rb index c4513b6..826eb1c 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -54,6 +54,7 @@ class Account < ApplicationRecord Download.where(project: projects), User.joins(:project_memberships).where(project_memberships: { project: projects }), Broadcast.where(project: projects), + TaskRequest.where(project: projects), ZoomMeeting.where(project: projects), MedicalRelease.where(project: projects), MiscRelease.where(project: projects), @@ -82,6 +83,10 @@ class Account < ApplicationRecord plan_uid.to_s == "me_suite" || plan_uid.to_s == "releaseme" end + def taskme_enabled? + plan_uid.to_s == "me_suite" || plan_uid.to_s == "taskme" + end + def plan_name case plan_uid.to_s when "deliverme" @@ -90,6 +95,8 @@ class Account < ApplicationRecord "DirectME" when "releaseme" "ReleaseME" + when "taskme" + "TaskME" when "me_suite" "ME Suite" end diff --git a/app/models/project.rb b/app/models/project.rb index d972194..b720b32 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -24,6 +24,7 @@ class Project < ApplicationRecord has_many :downloads, dependent: :destroy has_many :broadcasts, dependent: :destroy has_many :zoom_meetings, dependent: :destroy + has_many :task_requests, dependent: :destroy accepts_nested_attributes_for :project_memberships diff --git a/app/models/task_request.rb b/app/models/task_request.rb new file mode 100644 index 0000000..04345ac --- /dev/null +++ b/app/models/task_request.rb @@ -0,0 +1,10 @@ +class TaskRequest < ApplicationRecord + belongs_to :project + has_many_attached :files + + enum status: [:pending, :completed, :cancelled] + + scope :order_by_recent, -> { order(created_at: :desc) } + + validates :time_allowed, numericality: { only_integer: true, greater_than_or_equal_to: 2 } +end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index fafbb5f..32c0c70 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -36,4 +36,8 @@ class ProjectPolicy < ApplicationPolicy def show_downloads? show? end + + def show_task_results? + show? + end end diff --git a/app/policies/task_request_policy.rb b/app/policies/task_request_policy.rb new file mode 100644 index 0000000..3775941 --- /dev/null +++ b/app/policies/task_request_policy.rb @@ -0,0 +1,29 @@ +class TaskRequestPolicy < ApplicationPolicy + def index? + true + end + + def show? + true + end + + def create? + true + end + + def destroy? + true + end + + def update? + true + end + + def cancel? + true + end + + def open_deliverable? + true + end +end diff --git a/app/telemetry/analytics.rb b/app/telemetry/analytics.rb index 1f95495..11b2db0 100644 --- a/app/telemetry/analytics.rb +++ b/app/telemetry/analytics.rb @@ -151,6 +151,24 @@ class Analytics ) end end + + def track_create_task_request(user_agent:, user_ip:) + if analytics_enabled? + identify + track( + { + user_id: user.id, + event: "Task request created", + properties: { + account: account.try(:name), + account_id: account.try(:id), + user_agent: user_agent, + ip: user_ip, + }, + } + ) + end + end private diff --git a/app/views/admin/application/_side_nav.html.erb b/app/views/admin/application/_side_nav.html.erb index 00ea144..23ca54c 100644 --- a/app/views/admin/application/_side_nav.html.erb +++ b/app/views/admin/application/_side_nav.html.erb @@ -7,6 +7,9 @@ + diff --git a/app/views/admin/task_requests/_form.html.erb b/app/views/admin/task_requests/_form.html.erb new file mode 100644 index 0000000..e8601fd --- /dev/null +++ b/app/views/admin/task_requests/_form.html.erb @@ -0,0 +1,13 @@ +<%= errors_summary_for task_request %> + +<%= bootstrap_form_with model: model, local: true do |form| %> + <%= form.select :status, options_for_select(TaskRequest.statuses.except(:cancelled).keys, task_request.status), {}, class: "form-control custom-select" %> + <%= form.text_field :deliverable_url %> + +
+ <%= link_to t("shared.cancel"), [:admin, :task_requests], class: "col-3 text-reset" %> +
+ <%= form.submit class: class_string("btn btn-block", ["btn-success", "btn-primary"] => task_request.new_record?), data: { disable_with: t("shared.disable_with") } %> +
+
+<% end %> diff --git a/app/views/admin/task_requests/_task_request.html.erb b/app/views/admin/task_requests/_task_request.html.erb new file mode 100644 index 0000000..05763c6 --- /dev/null +++ b/app/views/admin/task_requests/_task_request.html.erb @@ -0,0 +1,32 @@ + + + <%= task_request.id %> + + + <%= task_request.project.account.name %> + + + <%= task_request.project.name %> + + + <%= task_request.created_at.strftime("%D") %> + + + <%= task_request.deadline.try(:strftime, '%D') %> + + + <%= task_request.time_allowed %> + + + <%= task_request.status.titleize %> + + +
+ <%= button_tag "Manage", class: "btn btn-light btn-sm dropdown-toggle border", data: { toggle: "dropdown", boundary: "window" }, aria: { haspopup: true, expanded: false } %> + +
+ + diff --git a/app/views/admin/task_requests/edit.html.erb b/app/views/admin/task_requests/edit.html.erb new file mode 100644 index 0000000..acacdf1 --- /dev/null +++ b/app/views/admin/task_requests/edit.html.erb @@ -0,0 +1,6 @@ +
+ <%= card_header text: "Edit Task Request", close_action_path: [:admin, :task_requests] %> +
+ <%= render "form", model: [:admin, @task_request], task_request: @task_request %> +
+
diff --git a/app/views/admin/task_requests/index.html.erb b/app/views/admin/task_requests/index.html.erb new file mode 100644 index 0000000..8afe67f --- /dev/null +++ b/app/views/admin/task_requests/index.html.erb @@ -0,0 +1,25 @@ +
+ + + + + + + + + + + + + + + <% if @task_requests.any? %> + <%= render @task_requests %> + <% else %> + + + + <% end %> + +
Task IDAccount NameProject NameCreated OnDeadlineTime AllowedStatus
<%= t(".empty") %>
+
diff --git a/app/views/admin/task_requests/show.html.erb b/app/views/admin/task_requests/show.html.erb new file mode 100644 index 0000000..07be9cb --- /dev/null +++ b/app/views/admin/task_requests/show.html.erb @@ -0,0 +1,49 @@ +
+ <%= card_header text: "Task Details", close_action_path: [:admin, :task_requests] %> +
+
+
+
+ <%= description_list_pair_for @task_request.project.account, :name, custom_label: "Account Name", append: ":" %> + <%= description_list_pair_for @task_request.project, :name, custom_label: "Project Name", append: ":" %> + <%= description_list_pair_for @task_request, :created_at, append: ":" %> + <%= description_list_pair_for @task_request, :user_email, append: ":" %> + <%= description_list_pair_for @task_request, :status, append: ":" %> +
+
+
+
+ <%= description_list_pair_for @task_request, :deadline, append: ":" %> + <%= description_list_pair_for @task_request, :time_allowed, append: ":" %> + <%= description_list_pair_for @task_request, :description, append: ":" %> + <%= description_list_pair_for @task_request, :additional_notes, append: ":" %> +
+
+
+

Files:

+
+ + + + + + + + + <% if @files.any? %> + <%= render partial: "task_requests/file", collection: @files %> + <% else %> + + + + <% end %> + +
Filename
<%= t(".empty") %>
+
+ <%= will_paginate @files %> +
+
+
+
+
+
diff --git a/app/views/application/_sidebar.html.erb b/app/views/application/_sidebar.html.erb index a78879e..df75cad 100644 --- a/app/views/application/_sidebar.html.erb +++ b/app/views/application/_sidebar.html.erb @@ -11,6 +11,12 @@