diff --git a/app/components/admin/custom_fields/hierarchy/change_item_parent_dialog_component.html.erb b/app/components/admin/custom_fields/hierarchy/change_item_parent_dialog_component.html.erb index c03a235ccc8..dc43525e528 100644 --- a/app/components/admin/custom_fields/hierarchy/change_item_parent_dialog_component.html.erb +++ b/app/components/admin/custom_fields/hierarchy/change_item_parent_dialog_component.html.erb @@ -28,18 +28,25 @@ See COPYRIGHT and LICENSE files for more details. ++#%> <%= - render Primer::Alpha::Dialog.new(id: dialog_id, title: I18n.t(:label_change_parent)) do |dialog| + render( + Primer::Alpha::Dialog.new( + id: dialog_id, + title: I18n.t(:label_change_parent), + size: :medium_portrait + ) + ) do |dialog| dialog.with_body do - primer_form_with(**form_arguments) do |f| - helpers.render_inline_form(f) do |form| - form.text_field(name: :new_parent, label: "New Parent") + primer_form_with(**form_arguments) do |form| + render( + Primer::OpenProject::FilterableTreeView.new(form_arguments: { builder: form, name: :new_parent }) + ) do |tree_view| + add_sub_tree(tree_view, hierarchy_items) end end end dialog.with_footer(show_divider: true) do concat(render(Primer::Beta::Button.new(data: { close_dialog_id: dialog_id })) { I18n.t(:button_cancel) }) - concat( render( Primer::Beta::Button.new( diff --git a/app/components/admin/custom_fields/hierarchy/change_item_parent_dialog_component.rb b/app/components/admin/custom_fields/hierarchy/change_item_parent_dialog_component.rb index d75daaa5b96..d424ee57919 100644 --- a/app/components/admin/custom_fields/hierarchy/change_item_parent_dialog_component.rb +++ b/app/components/admin/custom_fields/hierarchy/change_item_parent_dialog_component.rb @@ -48,17 +48,50 @@ module Admin def form_arguments { - form_id:, + id: form_id, url: change_parent_custom_field_item_path(custom_field_id: @custom_field.id, id: @hierarchy_item.id), model: form_model, method: :post } end + def hierarchy_items + hashed_hierarchy = @custom_field.hierarchy_root.hash_tree + hashed_hierarchy.keys.first.label = @custom_field.name + + hashed_hierarchy + end + + def add_sub_tree(tree, hierarchy_hash) + hierarchy_hash.each do |item, child_hash| + if child_hash.empty? + tree.with_leaf(**item_options(item)) + else + expanded = current?(item) || child_hash.any? { |child, _| current?(child) } + + tree.with_sub_tree(expanded:, **item_options(item)) do |sub_tree| + add_sub_tree(sub_tree, child_hash) + end + end + end + end + private def form_model - CustomField::Hierarchy::Forms::NewParentFormModel.new(new_parent: nil) + CustomField::Hierarchy::Forms::NewParentFormModel.new(new_parent: []) + end + + def item_options(item) + { + label: item.label, + current: current?(item), + value: item.id + } + end + + def current?(item) + item.id == @hierarchy_item.id end end end diff --git a/app/controllers/admin/custom_fields/hierarchy/items_controller.rb b/app/controllers/admin/custom_fields/hierarchy/items_controller.rb index b5fd7ba86b5..03de873e437 100644 --- a/app/controllers/admin/custom_fields/hierarchy/items_controller.rb +++ b/app/controllers/admin/custom_fields/hierarchy/items_controller.rb @@ -33,6 +33,7 @@ module Admin module Hierarchy class ItemsController < ApplicationController include OpTurbo::ComponentStream + include Dry::Monads[:result] layout :admin_or_frame_layout model_object CustomField @@ -103,11 +104,25 @@ module Admin end def change_parent - permitted = params.expect(custom_field_hierarchy_forms_new_parent_form_model: [:new_parent]) - new_parent = CustomField::Hierarchy::Item.including_children.find(permitted[:new_parent]) - item_service.move_item(item: @active_item, new_parent:) + result = parse_parent_input(new_parent_params).bind do |new_parent| + validate_new_parent(new_parent).bind do + item_service.move_item(item: @active_item, new_parent:) + end + end - redirect_to(custom_field_item_path(@custom_field, new_parent), status: :see_other) + result.either( + ->(result) do + redirect_to( + custom_field_item_path(@custom_field, result.parent), + status: :see_other, + notice: I18n.t(:notice_successful_update) + ) + end, + ->(error) do + render_error_flash_message_via_turbo_stream(message: error) + respond_with_turbo_streams(&:html) + end + ) end def destroy @@ -147,6 +162,10 @@ module Admin input end + def new_parent_params + params.require(:custom_field_hierarchy_forms_new_parent_form_model).require(:new_parent) + end + def create_contract case @custom_field.field_format when "hierarchy" @@ -184,6 +203,29 @@ module Admin end end + def parse_parent_input(new_parent_input) + case new_parent_input + in [new_parent] + input = MultiJson.load(new_parent, symbolize_keys: true)[:value] + new_parent = CustomField::Hierarchy::Item.including_children.find_by(id: input) + + if new_parent.present? + Success(new_parent) + else + Failure("Cannot find parent with id: #{input}") + end + else + Failure("Invalid input: #{new_parent_input}") + end + end + + def validate_new_parent(new_parent) + return Failure("Parent must not be the same as before.") if @active_item.parent.id == new_parent.id + return Failure("Parent must not be the current item.") if @active_item.id == new_parent.id + + Success() + end + def find_model_object @object = CustomField.hierarchy_root_and_children.find(params[:custom_field_id]) @custom_field = @object