Skip to content

Commit

Permalink
Merge pull request #15971 from opf/implementation/55163-build-sharing…
Browse files Browse the repository at this point in the history
…-modal

[#55163] Build project-list sharing modal
  • Loading branch information
klaustopher authored Jul 4, 2024
2 parents 52cff64 + dee8171 commit 3464147
Show file tree
Hide file tree
Showing 48 changed files with 969 additions and 394 deletions.
38 changes: 13 additions & 25 deletions app/components/projects/index_page_header_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<% if show_state? %>
<%=
render(Primer::OpenProject::PageHeader.new) do |header|
header.with_title(data: { 'test-selector': 'project-query-name'}) { page_title }
header.with_title(data: { 'test-selector': 'project-query-name' }) { page_title }
header.with_breadcrumbs(breadcrumb_items, selected_item_font_weight: current_breadcrumb_element == page_title ? :bold : :normal)

if can_save?
Expand All @@ -21,6 +21,17 @@
)
end

if can_access_shares?
header.with_action_icon_button(
tag: :a,
href: dialog_project_query_members_path(query),
icon: "share-android",
mobile_icon: "share-android",
label: t(:label_share),
data: { controller: "async-dialog" }
)
end

if can_toggle_favor?
if currently_favored?
header.with_action_icon_button(
Expand Down Expand Up @@ -115,29 +126,6 @@
end

if query.persisted?
# TODO: Remove section when the sharing modal is implemented (https://community.openproject.org/projects/openproject/work_packages/55163)
if can_publish?
if query.public?
menu.with_item(
label: t(:button_unpublish),
scheme: :danger,
href: unpublish_project_query_path(query),
content_arguments: { data: { method: :post } }
) do |item|
item.with_leading_visual_icon(icon: 'eye-closed')
end
else
menu.with_item(
label: t(:button_publish),
scheme: :default,
href: publish_project_query_path(query),
content_arguments: { data: { method: :post } }
) do |item|
item.with_leading_visual_icon(icon: 'eye')
end
end
end

menu.with_item(
label: t(:button_delete),
scheme: :danger,
Expand All @@ -155,7 +143,7 @@
<% else %>
<%=
render(Primer::OpenProject::PageHeader.new) do |header|
header.with_title(data: { 'test-selector': 'project-query-name'}) do
header.with_title(data: { 'test-selector': 'project-query-name' }) do
primer_form_with(model: query,
url: @query.new_record? ? project_queries_path(projects_query_params) : project_query_path(@query, projects_query_params),
scope: 'query',
Expand Down
22 changes: 6 additions & 16 deletions app/components/projects/index_page_header_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,35 +77,25 @@ def can_save?
return false unless query.persisted?
return false unless query.changed?

if query.public?
current_user.allowed_globally?(:manage_public_project_queries)
else
query.user == current_user
end
query.editable?
end

def can_rename?
return false unless current_user.logged?
return false unless query.persisted?
return false if query.changed?

if query.public?
current_user.allowed_globally?(:manage_public_project_queries)
else
query.user == current_user
end
end

def can_publish?
OpenProject::FeatureDecisions.project_list_sharing_active? &&
current_user.allowed_globally?(:manage_public_project_queries) &&
query.persisted?
query.editable?
end

def show_state?
state == :show
end

def can_access_shares?
query.persisted? && OpenProject::FeatureDecisions.project_list_sharing_active?
end

def can_toggle_favor? = query.persisted?

def currently_favored? = query.favored_by?(current_user)
Expand Down
6 changes: 3 additions & 3 deletions app/components/shares/bulk_permission_button_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@

module Shares
class BulkPermissionButtonComponent < ApplicationComponent # rubocop:disable OpenProject/AddPreviewForViewComponent
def initialize(entity:, available_roles:)
def initialize(strategy:)
super

@entity = entity
@available_roles = available_roles
@entity = strategy.entity
@available_roles = strategy.available_roles
end

def update_path
Expand Down
2 changes: 1 addition & 1 deletion app/components/shares/counter_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# There's no point in rendering the BulkSelectionCounterComponent even if
# I'm able to manage shares if the only user that the work package is
# currently shared is myself, since I'm not able to manage my own share.
if sharing_manageable? && shared_with_anyone_else_other_than_myself?
if strategy.manageable? && shared_with_anyone_else_other_than_myself?
render(Shares::BulkSelectionCounterComponent.new(count:))
else
render(Shares::ShareCounterComponent.new(count:))
Expand Down
14 changes: 5 additions & 9 deletions app/components/shares/counter_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,22 @@ class CounterComponent < ApplicationComponent # rubocop:disable OpenProject/AddP
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers

def initialize(entity:,
count:,
sharing_manageable:)
def initialize(strategy:, count:)
super

@entity = entity
@strategy = strategy
@entity = strategy.entity
@count = count
@sharing_manageable = sharing_manageable
end

private

attr_reader :entity, :count

def sharing_manageable? = @sharing_manageable
attr_reader :entity, :count, :strategy

def shared_with_anyone_else_other_than_myself?
Member.of_entity(@entity)
.where.not(principal: User.current)
.any?
.exists?
end
end
end
15 changes: 15 additions & 0 deletions app/components/shares/empty_state_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<%= render(Primer::Beta::Blankslate.new) do |component|
component.with_visual_icon(icon: blankslate_config[:icon], size: :medium)
component.with_heading(tag: :h2).with_content(
blankslate_config[:heading_text],
)
component.with_description do
flex_layout do |flex|
flex.with_row(mb: 2) do
render(Primer::Beta::Text.new(color: :subtle)) do
blankslate_config[:description_text]
end
end
end
end
end %>
68 changes: 68 additions & 0 deletions app/components/shares/empty_state_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 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 Shares
class EmptyStateComponent < ApplicationComponent # rubocop:disable OpenProject/AddPreviewForViewComponent
include OpPrimer::ComponentHelpers

def initialize(strategy:)
super

@strategy = strategy
@entity = strategy.entity
end

private

attr_reader :strategy, :entity

def blankslate_config
@blankslate_config ||= if params[:filters].blank?
unfiltered_blankslate_config
else
filtered_blankslate_config
end
end

def unfiltered_blankslate_config
{
icon: :people,
heading_text: I18n.t("sharing.text_empty_state_header"),
description_text: I18n.t("sharing.text_empty_state_description", entity: @entity.class.model_name.human)
}
end

def filtered_blankslate_config
{
icon: :search,
heading_text: I18n.t("sharing.text_empty_search_header"),
description_text: I18n.t("sharing.text_empty_search_description")
}
end
end
end
18 changes: 8 additions & 10 deletions app/components/shares/invite_user_form_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
<%=
component_wrapper do
if @sharing_manageable
if strategy.manageable?
primer_form_with(
model: new_share,
url: url_for([@entity, Member]),
data: { controller: 'user-limit ' \
'shares--user-selected',
url: url_for([entity, Member]),
data: { controller: 'user-limit shares--user-selected',
'application-target': 'dynamic',
'user-limit-open-seats-value': OpenProject::Enterprise.open_seats_count,
action: 'submit->shares--user-selected#ensureUsersSelected' }
) do |form|
grid_layout('invite-user-form',
tag: :div) do |invite_form|
grid_layout('invite-user-form', tag: :div) do |invite_form|
invite_form.with_area('invitee') do
render(Shares::Invitee.new(form))
end

invite_form.with_area('permission') do
render(Shares::PermissionButtonComponent.new(
share: new_share,
available_roles: @available_roles,
available_roles: strategy.available_roles,
form_arguments: { builder: form, name: "role_id" },
data: { 'test-selector': 'op-share-dialog-invite-role' })
)
Expand All @@ -46,7 +44,7 @@
I18n.t(
"sharing.warning_user_limit_reached#{'_admin' if User.current.admin?}",
upgrade_url: OpenProject::Enterprise.upgrade_url,
entity: @entity.model_name.human
entity: entity.model_name.human
).html_safe
end
end
Expand All @@ -65,7 +63,7 @@

no_selected_user_row.with_column do
render(Primer::Beta::Text.new(color: :danger)) do
I18n.t("sharing.warning_no_selected_user", entity: @entity.model_name.human)
I18n.t("sharing.warning_no_selected_user", entity: entity.model_name.human)
end
end
end
Expand Down Expand Up @@ -96,7 +94,7 @@
end
end
else
render(Primer::Alpha::Banner.new(icon: :info)) { I18n.t('sharing.denied', entities: @entity.model_name.human(count: 2)) }
render(Primer::Alpha::Banner.new(icon: :info)) { I18n.t('sharing.denied', entities: entity.model_name.human(count: 2)) }
end
end
%>
16 changes: 7 additions & 9 deletions app/components/shares/invite_user_form_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,24 @@ class InviteUserFormComponent < ApplicationComponent # rubocop:disable OpenProje
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers

def initialize(entity:,
available_roles:,
sharing_manageable:,
errors: nil)
attr_reader :entity, :strategy, :errors

def initialize(strategy:, errors: nil)
super

@entity = entity
@available_roles = available_roles
@sharing_manageable = sharing_manageable
@strategy = strategy
@entity = strategy.entity
@errors = errors
end

def new_share
@new_share ||= Member.new(entity: @entity, roles: [Role.new(id: default_role[:value])])
@new_share ||= Member.new(entity:, roles: [Role.new(id: default_role[:value])])
end

private

def default_role
@available_roles.find { |role_hash| role_hash[:default] } || @available_roles.first
strategy.available_roles.find { |role_hash| role_hash[:default] } || strategy.available_roles.first
end
end
end
Loading

0 comments on commit 3464147

Please sign in to comment.