Files
openproject/app/components/projects/row_component.rb
T

370 lines
11 KiB
Ruby

# 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.
#++
module Projects
class RowComponent < ::RowComponent
delegate :favored_project_ids, to: :table
def project
model.first
end
def level
model.last
end
# Hierarchy cell is just a placeholder
def hierarchy
""
end
def favored
render(Primer::Beta::IconButton.new(
icon: currently_favored? ? "star-fill" : "star",
scheme: :invisible,
mobile_icon: currently_favored? ? "star-fill" : "star",
size: :medium,
tag: :a,
tooltip_direction: :e,
href: helpers.build_favorite_path(project, format: :html),
data: { method: currently_favored? ? :delete : :post },
classes: currently_favored? ? "op-primer--star-icon " : "op-project-row-component--favorite",
label: currently_favored? ? I18n.t(:button_unfavorite) : I18n.t(:button_favorite),
aria: { label: currently_favored? ? I18n.t(:button_unfavorite) : I18n.t(:button_favorite) },
test_selector: "project-list-favorite-button"
))
end
def currently_favored?
@currently_favored ||= favored_project_ids.include?(project.id)
end
def column_value(column)
if custom_field_column?(column)
custom_field_column(column)
else
send(column.attribute)
end
end
def custom_field_column(column)
return nil unless user_can_view_project?
cf = column.custom_field
custom_value = project.formatted_custom_value_for(cf)
if cf.field_format == "text" && custom_value.present?
render OpenProject::Common::AttributeComponent.new(
"dialog-#{project.id}-cf-#{cf.id}",
cf.name,
custom_value,
formatted: true
)
elsif custom_value.is_a?(Array)
safe_join(Array(custom_value).compact_blank, ", ")
else
custom_value
end
end
def created_at
helpers.format_date(project.created_at)
end
def latest_activity_at
helpers.format_date(project.latest_activity_at)
end
def required_disk_space
return "" unless project.required_disk_space.to_i > 0
number_to_human_size(project.required_disk_space, precision: 2)
end
def name
content = content_tag(:i, "", class: "projects-table--hierarchy-icon")
if project.archived?
content << " "
content << content_tag(:span, I18n.t("project.archive.archived"), class: "archived-label")
end
content << " "
content << helpers.link_to_project(project, {}, {}, false)
content
end
def project_status
return nil unless user_can_view_project?
content = "".html_safe
status_code = project.status_code
if status_code
classes = helpers.project_status_css_class(status_code)
content << content_tag(:span, "", class: "project-status--bulb -inline #{classes}")
content << content_tag(:span, helpers.project_status_name(status_code), class: "project-status--name #{classes}")
end
content
end
def status_explanation
return nil unless user_can_view_project?
if project.status_explanation.present? && project.status_explanation
render OpenProject::Common::AttributeComponent.new("dialog-#{project.id}-status-explanation",
I18n.t("activerecord.attributes.project.status_explanation"),
project.status_explanation)
end
end
def description
return nil unless user_can_view_project?
if project.description.present?
render OpenProject::Common::AttributeComponent.new("dialog-#{project.id}-description",
I18n.t("activerecord.attributes.project.description"),
project.description)
end
end
def public
helpers.checked_image project.public?
end
def row_css_class
classes = %w[basics context-menu--reveal op-project-row-component]
classes << project_css_classes
classes << row_css_level_classes
classes.join(" ")
end
def row_css_id
"project-#{project.id}"
end
def row_css_level_classes
if level > 0
"idnt idnt-#{level}"
else
""
end
end
def project_css_classes
s = " project ".html_safe
s << " root" if project.root?
s << " child" if project.child?
s << (project.leaf? ? " leaf" : " parent")
s
end
def column_css_class(column)
"#{column.attribute} #{additional_css_class(column)}"
end
def additional_css_class(column)
if column.attribute == :name
"project--hierarchy #{project.archived? ? 'archived' : ''}"
elsif %i[status_explanation description].include?(column.attribute)
"project-long-text-container"
elsif column.attribute == :favored
"-w-abs-45"
elsif custom_field_column?(column)
cf = column.custom_field
formattable = cf.field_format == "text" ? " project-long-text-container" : ""
"format-#{cf.field_format}#{formattable}"
end
end
def button_links
if more_menu_items.empty?
[]
else
[action_menu]
end
end
def action_menu
render(Primer::Alpha::ActionMenu.new(test_selector: "project-list-row--action-menu")) do |menu|
menu.with_show_button(scheme: :invisible,
size: :small,
icon: :"kebab-horizontal",
"aria-label": t(:label_open_menu),
tooltip_direction: :w)
more_menu_items.each do |action_options|
action_options => { scheme:, label:, icon:, **button_options }
menu.with_item(scheme:,
label:,
test_selector: "project-list-row--action-menu-item",
content_arguments: button_options) do |item|
item.with_leading_visual_icon(icon:) if icon
end
end
end
end
def more_menu_items
@more_menu_items ||= [more_menu_subproject_item,
more_menu_settings_item,
more_menu_activity_item,
more_menu_favorite_item,
more_menu_unfavorite_item,
more_menu_archive_item,
more_menu_unarchive_item,
more_menu_copy_item,
more_menu_delete_item].compact
end
def more_menu_favorite_item
return if currently_favored?
{
scheme: :default,
icon: "star",
href: helpers.build_favorite_path(project, format: :html),
data: { method: :post },
label: I18n.t(:button_favorite),
aria: { label: I18n.t(:button_favorite) }
}
end
def more_menu_unfavorite_item
return unless currently_favored?
{
scheme: :default,
icon: "star-fill",
size: :medium,
href: helpers.build_favorite_path(project, format: :html),
data: { method: :delete },
classes: "op-primer--star-icon",
label: I18n.t(:button_unfavorite),
aria: { label: I18n.t(:button_unfavorite) }
}
end
def more_menu_subproject_item
if User.current.allowed_in_project?(:add_subprojects, project)
{
scheme: :default,
icon: :plus,
label: I18n.t(:label_subproject_new),
href: new_project_path(parent_id: project.id)
}
end
end
def more_menu_settings_item
if User.current.allowed_in_project?({ controller: "/projects/settings/general", action: "show", project_id: project.id },
project)
{
scheme: :default,
icon: :gear,
label: I18n.t(:label_project_settings),
href: project_settings_general_path(project)
}
end
end
def more_menu_activity_item
if User.current.allowed_in_project?(:view_project_activity, project)
{
scheme: :default,
icon: :check,
label: I18n.t(:label_project_activity),
href: project_activity_index_path(project, event_types: ["project_attributes"])
}
end
end
def more_menu_archive_item
if User.current.allowed_in_project?(:archive_project, project) && project.active?
{
scheme: :default,
icon: :lock,
label: I18n.t(:button_archive),
href: project_archive_path(project, status: params[:status]),
data: {
confirm: t("project.archive.are_you_sure", name: project.name),
method: :post
}
}
end
end
def more_menu_unarchive_item
if User.current.admin? && project.archived? && (project.parent.nil? || project.parent.active?)
{
scheme: :default,
icon: :unlock,
label: I18n.t(:button_unarchive),
href: project_archive_path(project, status: params[:status]),
data: { method: :delete }
}
end
end
def more_menu_copy_item
if User.current.allowed_in_project?(:copy_projects, project) && !project.archived?
{
scheme: :default,
icon: :copy,
label: I18n.t(:button_copy),
href: copy_project_path(project)
}
end
end
def more_menu_delete_item
if User.current.admin
{
scheme: :danger,
icon: :trash,
label: I18n.t(:button_delete),
href: confirm_destroy_project_path(project)
}
end
end
def user_can_view_project?
User.current.allowed_in_project?(:view_project, project)
end
def custom_field_column?(column)
column.is_a?(::Queries::Projects::Selects::CustomField)
end
end
end