Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#58345] Primerize Relations tab #16989

Draft
wants to merge 29 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e762f02
Render relations tab via server-side rendered turbo frame
aaron-contreras Oct 18, 2024
c4f0d23
Primerize relations tab contents
aaron-contreras Oct 18, 2024
5088035
i18n of relation labels and tab section
aaron-contreras Oct 24, 2024
34fea8e
Group relations by directional semantics
aaron-contreras Oct 28, 2024
f698bb9
Add ability to delete relations from within the tab
aaron-contreras Oct 30, 2024
38fa140
Add ability to edit relations
aaron-contreras Oct 31, 2024
ee6ab4b
Make child relationships uneditable
aaron-contreras Nov 4, 2024
ee18cd3
Clean up controller baggage
aaron-contreras Nov 4, 2024
742d12c
Add spec for WorkPackageRelationsTabController
aaron-contreras Nov 4, 2024
b9e17bd
Add spec for WorkPackageRelationsController
aaron-contreras Nov 4, 2024
dd92825
Add spec for WorkPackageChildrenController
aaron-contreras Nov 4, 2024
2d3d566
Allow creating new relations from relations tab
aaron-contreras Nov 4, 2024
bdad2af
Split Child vs Relation and Create vs Edit
aaron-contreras Nov 5, 2024
d7d5c24
Add spec for attaching a child to a work package
aaron-contreras Nov 6, 2024
af9200a
Render start and end dates for successor relationships
aaron-contreras Nov 6, 2024
8a3bb0a
Render start and end dates for predecessor relationships
aaron-contreras Nov 6, 2024
c0c4b14
Trigger relations count re-rendering on Turbo <-> Angular sources
aaron-contreras Nov 8, 2024
ff4e9c0
Remove old relations section
aaron-contreras Nov 11, 2024
c1eae48
Fix translation typo
aaron-contreras Nov 11, 2024
0c6c61f
Allow only relatable nodes in autocomplete results
aaron-contreras Nov 11, 2024
64f272a
Fix old specs
aaron-contreras Nov 12, 2024
03ab049
Update primerized counters on tabs when changes are detected on relat…
aaron-contreras Nov 12, 2024
495a68f
Check for permissions to render CRUD actions on tab
aaron-contreras Nov 12, 2024
dbb42ea
Fix more specs
aaron-contreras Nov 12, 2024
41e1662
Fix more specs for the tab
aaron-contreras Nov 12, 2024
a10d97d
Fix/remove remaining broken specs and those no longer relevant
aaron-contreras Nov 13, 2024
039f651
Remove unused aR import
aaron-contreras Nov 13, 2024
0f86ad7
Fix safari layouting issuse
aaron-contreras Nov 13, 2024
f7f2ec8
Fix/stabilize board navigation specs
aaron-contreras Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/components/_index.sass
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
@import "projects/row_component"
@import "op_primer/border_box_table_component"
@import "work_packages/exports/modal_dialog_component"
@import "work_package_relations_tab/index_component"
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<%=
render(Primer::Alpha::Dialog.new(title: dialog_title,
size: :xlarge,
id: DIALOG_ID)) do |d|
d.with_header(variant: :large)
d.with_body do
render(WorkPackageRelationsTab::AddWorkPackageChildFormComponent.new(
work_package: @work_package
))
end
d.with_footer do
component_collection do |buttons|
buttons.with_component(Primer::ButtonComponent.new(
data: {
'close-dialog-id': DIALOG_ID
}
)) do
t("button_cancel")
end
buttons.with_component(Primer::ButtonComponent.new(
scheme: :primary,
form: FORM_ID,
data: { turbo: true },
type: :submit)) do
t("button_save")
end
end
end
end
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class WorkPackageRelationsTab::AddWorkPackageChildDialogComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers

I18N_NAMESPACE = "work_package_relations_tab"
DIALOG_ID = "add-work-package-child-dialog"
FORM_ID = "add-work-package-child-form"

attr_reader :work_package

def initialize(work_package:)
super()

@work_package = work_package
end

private

def dialog_title
child_label = t("#{I18N_NAMESPACE}.relations.label_child_singular")
t("#{I18N_NAMESPACE}.label_add_x", x: child_label)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<%= component_wrapper do %>
<%= primer_form_with(
id: FORM_ID,
model: WorkPackage.new,
**submit_url_options,
data: { turbo: true }
) do |f| %>
<%# Form fields section %>
<%= flex_layout(my: 3) do |flex|
flex.with_row do
if @base_errors&.any?
render(Primer::Alpha::Banner.new(mb: 3, icon: :stop, scheme: :danger)) { @base_errors.join("\n") }
end
end
flex.with_row do
i18n_namespace = I18N_NAMESPACE
dialog_id = DIALOG_ID
id_field_test_selector = ID_FIELD_TEST_SELECTOR
render_inline_form(f) do |my_form|
my_form.work_package_autocompleter(
name: :id,
label: WorkPackage.model_name.human,
visually_hide_label: false,
autocomplete_options: {
resource: 'work_packages',
searchKey: 'subjectOrId',
openDirectly: false,
focusDirectly: false,
dropdownPosition: 'bottom',
appendTo: "##{dialog_id}",
data: { test_selector: id_field_test_selector }
}
)
end
end
end %>
<% end %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class WorkPackageRelationsTab::AddWorkPackageChildFormComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers

DIALOG_ID = "add-work-package-child-dialog"
FORM_ID = "add-work-package-child-form"
ID_FIELD_TEST_SELECTOR = "work-package-child-form-id"
I18N_NAMESPACE = "work_package_relations_tab"

def initialize(work_package:, base_errors: nil)
super()

@work_package = work_package
@base_errors = base_errors
end

def submit_url_options
{ method: :post,
url: work_package_children_path(@work_package) }
end
end
131 changes: 131 additions & 0 deletions app/components/work_package_relations_tab/index_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<%= component_wrapper(tag: "turbo-frame") do %>
<%=
if should_render_add_relations?
flex_layout(justify_content: :space_between,
align_items: :center,
mb: 4,
style: "gap: 1rem;") do |action_bar|
action_bar.with_column do
render(Primer::Beta::Text.new(color: :muted)) do
t("#{I18N_NAMESPACE}.index.action_bar_title")
end
end
# Prevent the menu from overflowing on Safari
action_bar.with_column(style: "flex: 0 0 auto;") do
render(Primer::Alpha::ActionMenu.new(test_selector: NEW_RELATION_ACTION_MENU,
menu_id: NEW_RELATION_ACTION_MENU)) do |menu|
menu.with_show_button do |button|
button.with_leading_visual_icon(icon: :"plus")
button.with_trailing_action_icon(icon: :"triangle-down")
t(:label_relation)
end
Relation::TYPES.each do |relation_type, type_configuration_hash|
label_key = "#{I18N_NAMESPACE}.relations.#{type_configuration_hash[:name]}_singular"
menu.with_item(
label: t(label_key).capitalize,
href: new_relation_path(relation_type:),
test_selector: new_button_test_selector(relation_type:),
content_arguments: {
data: { turbo_stream: true }
}
) do |item|
item.with_description.with_content(t("#{I18N_NAMESPACE}.relations.#{relation_type}_description"))
end
end

if should_render_add_child?
menu.with_item(
label: t("#{I18N_NAMESPACE}.relations.label_child_singular").capitalize,
href: new_work_package_child_path(@work_package),
test_selector: new_button_test_selector(relation_type: :child),
content_arguments: {
data: { turbo_stream: true }
}
) do |item|
item.with_description.with_content(t("#{I18N_NAMESPACE}.relations.child_description"))
end
end
end
end
end
elsif should_only_render_add_child?
flex_layout(justify_content: :space_between, align_items: :center, mb: 4) do |action_bar|
action_bar.with_column do
render(Primer::Beta::Text.new(color: :muted)) do
t("#{I18N_NAMESPACE}.index.action_bar_title")
end
end
# Prevent the menu from overflowing on Safari
action_bar.with_column(style: "flex: 0 0 auto;") do
render(Primer::Alpha::ActionMenu.new(test_selector: NEW_RELATION_ACTION_MENU,
menu_id: NEW_RELATION_ACTION_MENU)) do |menu|
menu.with_show_button do |button|
button.with_leading_visual_icon(icon: :"plus")
button.with_trailing_action_icon(icon: :"triangle-down")
t(:label_relation)
end

menu.with_item(
label: t("#{I18N_NAMESPACE}.relations.label_child_singular").capitalize,
href: new_work_package_child_path(@work_package),
test_selector: new_button_test_selector(relation_type: :child),
content_arguments: {
data: { turbo_stream: true }
}
) do |item|
item.with_description.with_content(t("#{I18N_NAMESPACE}.relations.child_description"))
end
end
end
end
end
%>
<%=
flex_layout(classes: "work-package-relations-tab--list-container", mb: 3) do |flex|
if any_relations?
key_namespace = "#{I18N_NAMESPACE}.relations"

# Relations
directionally_aware_grouped_relations.each do |relation_type, relations_of_type|
base_key = "#{key_namespace}.label_#{relation_type}"

flex.with_row do
render_relation_group(
title: t("#{base_key}_plural").capitalize,
relation_type:,
items: relations_of_type
) do |relation|
render(WorkPackageRelationsTab::RelationComponent.new(work_package:,
relation:))
end
end
end

# Children
if children.any?
base_key = "#{key_namespace}.label_child"

flex.with_row do
render_relation_group(
title: t("#{base_key}_plural").capitalize,
relation_type: :children,
items: children
) do |child|
render(WorkPackageRelationsTab::RelationComponent.new(work_package:,
relation: nil,
child:))
end
end
end
else
flex.with_row do
render Primer::Beta::Blankslate.new(border: true) do |component|
component.with_visual_icon(icon: "package-dependents")
component.with_heading(tag: :h2).with_content(t("#{I18N_NAMESPACE}.index.blankslate_heading"))
component.with_description { t("#{I18N_NAMESPACE}.index.blankslate_description") }
end
end
end
end
%>
<% end %>
Loading