Skip to content

Commit

Permalink
Merge pull request #16365 from opf/implementation/56284-implement-edi…
Browse files Browse the repository at this point in the history
…t-project-folder-button-in-context-menu

[#56284] Implement "Edit project folder" button in context menu
  • Loading branch information
akabiru authored Aug 8, 2024
2 parents 2ea4120 + eaf0fbc commit 8f7efcd
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,36 @@ See COPYRIGHT and LICENSE files for more details.
primer_form_with(
class: "op-new-project-storage-form",
model: @project_storage,
url: admin_settings_storage_project_storages_path(project_storage.storage),
url:,
data: { turbo: true },
method: :post
method:
) do |form|
concat(render(Primer::Alpha::Dialog::Body.new(
id: dialog_body_id,
test_selector: dialog_body_id,
aria: { label: title }
aria: { label: aria_label }
)) do
flex_layout do |flex|
flex.with_row do
angular_component_tag 'opce-custom-modal-overlay'
end

flex.with_row(mb: 3) do
render(Storages::Admin::Storages::AddProjectsAutocompleterForm.new(form, project_storage:))
end
if new_record?
flex.with_row(mb: 3) do
render(Storages::Admin::Storages::AddProjectsAutocompleterForm.new(form, project_storage:))
end

flex.with_row(mb: 3) do
render(Primer::Alpha::ActionList::Divider.new(scheme: :subtle))
end
flex.with_row(mb: 3) do
render(Primer::Alpha::ActionList::Divider.new(scheme: :subtle))
end

flex.with_row(mb: 2) do
render(Primer::Beta::Heading.new(tag: :h4)) { t(:"storages.label_project_folder") }
flex.with_row(mb: 2) do
render(Primer::Beta::Heading.new(tag: :h4)) { t(:"storages.label_project_folder") }
end
end

flex.with_row(mb: 3) do
render(Primer::Beta::Text.new) { t(:"storages.help_texts.project_folder_bulk") }
render(Primer::Beta::Text.new) { help_text }
end

flex.with_row(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,56 @@
module Storages
module Admin
module Storages
class AddProjectsFormModalComponent < ApplicationComponent
class ProjectsStorageFormModalComponent < ApplicationComponent
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable
include StimulusHelper
include AngularHelper

def initialize(project_storage:, **)
def initialize(project_storage:, last_project_folders: {}, **)
@project_storage = project_storage
@last_project_folders = {}
@last_project_folders = last_project_folders
super(@project_storage, **)
end

private

attr_reader :project_storage, :storage

def dialog_id = Storages::AddProjectsModalComponent::DIALOG_ID
def dialog_body_id = Storages::AddProjectsModalComponent::DIALOG_BODY_ID
def dialog_id = Storages::ProjectsStorageModalComponent::DIALOG_ID
def dialog_body_id = Storages::ProjectsStorageModalComponent::DIALOG_BODY_ID

def title
I18n.t(:label_add_projects)
def url
if new_record?
admin_settings_storage_project_storages_path(project_storage.storage)
else
admin_settings_storage_project_storage_path(storage_id: project_storage.storage.id, id: project_storage.id)
end
end

def method = new_record? ? :post : :patch

def help_text
if new_record?
I18n.t(:"storages.help_texts.project_folder_bulk")
else
I18n.t(:"storages.help_texts.project_folder")
end
end

def aria_label
new_record? ? I18n.t(:label_add_projects) : I18n.t(:"project_storages.edit_project_folder.label")
end

def cancel_button_text
I18n.t("button_cancel")
end

def submit_button_text
I18n.t("button_add")
new_record? ? I18n.t("button_add") : I18n.t("button_save")
end

delegate :persisted?, :new_record?, to: :project_storage
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
variant: :large
)

render(::Storages::Admin::Storages::AddProjectsFormModalComponent.new(project_storage:))
render(::Storages::Admin::Storages::ProjectsStorageFormModalComponent.new(project_storage:, last_project_folders:))
end
%>
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@
module Storages
module Admin
module Storages
class AddProjectsModalComponent < ApplicationComponent
class ProjectsStorageModalComponent < ApplicationComponent
include OpTurbo::Streamable

DIALOG_ID = "storages--add-projects-modal".freeze
DIALOG_BODY_ID = "storages--add-projects-modal-body".freeze
DIALOG_ID = "storages--projects-storage-modal".freeze
DIALOG_BODY_ID = "storages--projects-storage-modal-body".freeze

def initialize(project_storage:, **)
def initialize(project_storage:, last_project_folders:, **)
@project_storage = project_storage
@storage = project_storage.storage
@last_project_folders = last_project_folders

super(@project_storage, **)
end

Expand All @@ -46,11 +48,17 @@ def initialize(project_storage:, **)
def dialog_id = DIALOG_ID
def dialog_body_id = DIALOG_BODY_ID

attr_reader :project_storage, :storage
attr_reader :project_storage, :storage, :last_project_folders

def title
I18n.t(:label_add_projects)
if new_record?
I18n.t(:label_add_projects)
else
I18n.t(:"storages.label_project_folder")
end
end

delegate :new_record?, to: :project_storage
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,57 @@ class RowComponent < Projects::RowComponent
include OpTurbo::Streamable

def project_folder_type
project_folder_mode = table.project_storages[project.id].project_folder_mode
project_folder_mode = project_storage.project_folder_mode
I18n.t("project_storages.project_folder_mode.#{project_folder_mode}")
end

def more_menu_items
@more_menu_items ||= [more_menu_detach_project].compact
return [] unless can_view_more_menu_items?

@more_menu_items ||= [more_menu_edit_project_storage, more_menu_detach_project]
end

private

def more_menu_edit_project_storage
{
scheme: :default,
icon: :pencil,
label: I18n.t("project_storages.edit_project_folder.label"),
href: edit_admin_settings_storage_project_storage_path(
storage_id: project_storage.storage.id,
id: project_storage.id
),
data: {
controller: "async-dialog"
}
}
end

def more_menu_detach_project
project = model.first
if User.current.admin && project.active?
{
scheme: :danger,
icon: :trash,
label: I18n.t("project_storages.remove_project.label"),
href: destroy_confirmation_dialog_admin_settings_storage_project_storage_path(
id: table.project_storages[project.id].id
),
data: {
controller: "async-dialog"
}
{
scheme: :danger,
icon: :trash,
label: I18n.t("project_storages.remove_project.label"),
href: destroy_confirmation_dialog_admin_settings_storage_project_storage_path(
id: project_storage.id
),
data: {
controller: "async-dialog"
}
end
}
end

def can_view_more_menu_items?
User.current.admin && project.active?
end

def project_storage
table.project_storages[project.id]
end

def project
model.first
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Storages::Admin::Storages::ProjectStoragesController < ApplicationControll

before_action :require_admin
before_action :find_model_object
before_action :load_project_storage, only: %i(destroy destroy_confirmation_dialog)
before_action :load_project_storage, only: %i(edit update destroy destroy_confirmation_dialog)

before_action :storage_projects_query, only: :index
before_action :ensure_storage_configured!, only: %i(new create)
Expand All @@ -52,7 +52,9 @@ class Storages::Admin::Storages::ProjectStoragesController < ApplicationControll
def index; end

def new
respond_with_dialog Storages::Admin::Storages::AddProjectsModalComponent.new(project_storage: @project_storage)
respond_with_dialog Storages::Admin::Storages::ProjectsStorageModalComponent.new(
project_storage: @project_storage, last_project_folders: {}
)
end

def create # rubocop:disable Metrics/AbcSize
Expand All @@ -66,13 +68,39 @@ def create # rubocop:disable Metrics/AbcSize
create_service.on_failure do
project_storage = create_service.result
project_storage.errors.merge!(create_service.errors)
component = Storages::Admin::Storages::AddProjectsFormModalComponent.new(project_storage:)
component = Storages::Admin::Storages::ProjectsStorageFormModalComponent.new(project_storage:)
update_via_turbo_stream(component:, status: :bad_request)
end

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

def edit
last_project_folders = Storages::LastProjectFolder
.where(project_storage: @project_storage)
.pluck(:mode, :origin_folder_id)
.to_h

respond_with_dialog Storages::Admin::Storages::ProjectsStorageModalComponent.new(
project_storage: @project_storage, last_project_folders:
)
end

def update
update_service = ::Storages::ProjectStorages::UpdateService
.new(user: current_user, model: @project_storage)
.call(params.to_unsafe_h[:storages_project_storage].merge(storage: @storage))

update_service.on_success { update_project_list_via_turbo_stream(url_for_action: :index) }

update_service.on_failure do
component = Storages::Admin::Storages::ProjectsStorageFormModalComponent.new(project_storage: @project_storage)
update_via_turbo_stream(component:, status: :bad_request)
end

respond_with_turbo_streams(status: update_service.success? ? :ok : :unprocessable_entity)
end

def destroy_confirmation_dialog
respond_with_dialog Storages::ProjectStorages::DestroyConfirmationDialogComponent.new(
storage: @storage,
Expand Down Expand Up @@ -109,7 +137,7 @@ def find_projects_to_activate_for_storage
else
initialize_project_storage
@project_storage.errors.add(:project_ids, :blank)
component = Storages::Admin::Storages::AddProjectsFormModalComponent.new(project_storage: @project_storage)
component = Storages::Admin::Storages::ProjectsStorageFormModalComponent.new(project_storage: @project_storage)
update_via_turbo_stream(component:, status: :bad_request)
respond_with_turbo_streams
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,13 @@ def validation_message

def project_folder_selection_classes
[].tap do |classes|
classes << "d-none" unless @project_storage.errors.include?(:project_folder_id)
classes << "d-none" unless show_project_folder_selection?
end
end

def show_project_folder_selection?
@project_storage.project_folder_manual? || @project_storage.errors.include?(:project_folder_id)
end
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions modules/storages/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ en:
permission_write_files: 'Automatically managed project folders: Write files'
project_module_storages: Files
project_storages:
edit_project_folder:
label: Edit project folder
project_folder_mode:
automatic: Automatically managed
inactive: No specific folder
Expand Down
2 changes: 1 addition & 1 deletion modules/storages/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
scope module: :storages do
resources :project_storages,
controller: "/storages/admin/storages/project_storages",
only: %i[index new create destroy] do
only: %i[index new create edit update destroy] do
get :destroy_confirmation_dialog, on: :member
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,19 @@

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

aggregate_failures "can edit the project folder" do
project_storages_index_page.click_menu_item_of("Edit project folder", project)

within("dialog") do
choose "No specific folder"
click_on "Save"
end

project_storages_index_page.within_the_table_row_containing(project.name) do
expect(page).to have_text("No specific folder")
end
end
end

context "when the user does not select a folder" do
Expand Down

0 comments on commit 8f7efcd

Please sign in to comment.