Skip to content

Commit

Permalink
feat[Op#57579] Define "Add projects" dialog for multi-project activation
Browse files Browse the repository at this point in the history
  • Loading branch information
akabiru committed Sep 4, 2024
1 parent 485bc85 commit 299aaa4
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,47 @@
#++

class Admin::CustomFields::CustomFieldProjectsController < ApplicationController
include OpTurbo::ComponentStream
include OpTurbo::DialogStreamHelper

layout "admin"

model_object CustomField

before_action :require_admin
before_action :find_model_object

before_action :available_project_custom_fields_query, only: :index
before_action :initialize_custom_field_project, only: :new
before_action :find_projects_to_activate_for_custom_field, only: :create

menu_item :custom_fields

def index
@available_project_custom_fields_query = ProjectQuery.new(
name: "custom-fields-projects-#{@custom_field.id}"
) do |query|
query.where(:available_project_custom_fields, "=", [@custom_field.id])
query.select(:name)
query.order("lft" => "asc")
def index; end

def new
respond_with_dialog Admin::CustomFields::CustomFieldProjects::NewCustomFieldProjectsModalComponent.new(
custom_field_project_mapping: @custom_field_project,
custom_field: @custom_field
)
end

def create
create_service = ::CustomFields::CustomFieldProjects::BulkCreateService
.new(user: current_user, projects: @projects, custom_field: @custom_field,
include_sub_projects: include_sub_projects?)
.call

create_service.on_success { render_project_list(url_for_action: :index) }

create_service.on_failure do
update_flash_message_via_turbo_stream(
message: join_flash_messages(create_service.errors),
full: true, dismiss_scheme: :hide, scheme: :danger
)
end

respond_to_with_turbo_streams(status: create_service.success? ? :ok : :unprocessable_entity)
end

def default_breadcrumb; end
Expand All @@ -56,8 +80,70 @@ def show_local_breadcrumb

private

def render_project_list(url_for_action: action_name)
update_via_turbo_stream(
component: Admin::CustomFields::CustomFieldProjects::TableComponent.new(
query: available_project_custom_fields_query,
params: { custom_field: @custom_field, url_for_action: }
)
)
end

def find_model_object(object_id = :custom_field_id)
super
@custom_field = @object
end

def find_projects_to_activate_for_custom_field
if (project_ids = params.to_unsafe_h[:custom_fields_project][:project_ids]).present?
@projects = Project.find(project_ids)
else
initialize_custom_field_project
@custom_field_project.errors.add(:project_ids, :blank)
update_via_turbo_stream(
component: Admin::CustomFields::CustomFieldProjects::NewCustomFieldProjectsFormModalComponent.new(
custom_field_project_mapping: @custom_field_project,
custom_field: @custom_field
),
status: :bad_request
)
respond_with_turbo_streams
end
rescue ActiveRecord::RecordNotFound
update_flash_message_via_turbo_stream message: t(:notice_project_not_found), full: true, dismiss_scheme: :hide,
scheme: :danger
update_project_list_via_turbo_stream

respond_with_turbo_streams
end

def update_project_list_via_turbo_stream(url_for_action: action_name)
update_via_turbo_stream(
component: Admin::CustomFields::CustomFieldProjects::TableComponent.new(
query: available_project_custom_fields_query,
params: { custom_field: @custom_field, url_for_action: }
)
)
end

def available_project_custom_fields_query
@available_project_custom_fields_query = ProjectQuery.new(
name: "custom-fields-projects-#{@custom_field.id}"
) do |query|
query.where(:available_project_custom_fields, "=", [@custom_field.id])
query.select(:name)
query.order("lft" => "asc")
end
end

def initialize_custom_field_project
@custom_field_project = ::CustomFields::CustomFieldProjects::SetAttributesService
.new(user: current_user, model: CustomFieldsProject.new, contract_class: EmptyContract)
.call(custom_field: @custom_field)
.result
end

def include_sub_projects?
ActiveRecord::Type::Boolean.new.cast(params.to_unsafe_h[:custom_fields_project][:include_sub_projects])
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class CustomFieldMappingForm < ApplicationForm
multiple: true,
dropdownPosition: "bottom",
disabledProjects: projects_with_custom_field_mapping,
inputName: "project_custom_field_project_mapping[project_ids]"
inputName: "#{input_name}[project_ids]"
}
)

Expand Down Expand Up @@ -82,5 +82,9 @@ def projects_with_custom_field_mapping
def join_table
@project_mapping.class
end

def input_name
join_table.model_name.singular
end
end
end
16 changes: 16 additions & 0 deletions app/views/admin/custom_fields/custom_field_projects/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ See COPYRIGHT and LICENSE files for more details.
selected: :custom_field_projects))
%>

<%=
render(Primer::OpenProject::SubHeader.new) do |component|
component.with_action_component do
render(Primer::Beta::Button.new(
scheme: :primary,
tag: :a,
href: new_custom_field_project_path(@custom_field),
data: { controller: "async-dialog" }
)) do |button|
button.with_leading_visual_icon(icon: 'op-include-projects')
I18n.t(:label_add_projects)
end
end
end unless @custom_field.required?
%>

<%=
if @custom_field.is_for_all?
render Primer::Beta::Blankslate.new(border: true) do |component|
Expand Down
4 changes: 4 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,10 @@ en:
minimum: "need to include at least one filter for principal, context or id with the '=' operator."
custom_field:
at_least_one_custom_option: "At least one option needs to be available."
custom_fields_project:
attributes:
project_ids:
blank: "Please select a project."
custom_actions:
only_one_allowed: "(%{name}) only one value is allowed."
empty: "(%{name}) value can't be empty."
Expand Down
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@
scope module: :custom_fields do
resources :projects,
controller: "/admin/custom_fields/custom_field_projects",
only: %i[index]
only: %i[index new create]
end
end
end
Expand Down
45 changes: 45 additions & 0 deletions spec/features/admin/custom_fields/custom_fields_project_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,51 @@
end
end

it "shows an error in the dialog when no project is selected before adding" do
create(:project)
expect(page).to have_no_css("dialog")
click_on "Add projects"

page.within("dialog") do
click_on "Add"

expect(page).to have_text("Please select a project.")
end
end

it "allows linking a project to a custom field" do
project = create(:project)
subproject = create(:project, parent: project)
click_on "Add projects"

within_test_selector("new-custom-field-projects-modal") do
autocompleter = page.find(".op-project-autocompleter")
autocompleter.fill_in with: project.name

expect(page).to have_no_text(archived_project.name)

find(".ng-option-label", text: project.name).click
check "Include sub-projects"

click_on "Add"
end

expect(page).to have_text(project.name)
expect(page).to have_text(subproject.name)

aggregate_failures "pagination links maintain the correct url" do
within ".op-pagination" do
pagination_links = page.all(".op-pagination--item-link")
expect(pagination_links.size).to be_positive

pagination_links.each do |pagination_link|
uri = URI.parse(pagination_link["href"])
expect(uri.path).to eq(custom_field_projects_path(custom_field))
end
end
end
end

context "and the project custom field is for all projects" do
shared_let(:custom_field) { create(:user_custom_field, is_for_all: true) }

Expand Down

0 comments on commit 299aaa4

Please sign in to comment.