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

[58242] Generalize the wp preview modal to be reused for users #16885

Merged
merged 12 commits into from
Oct 9, 2024
Merged
1 change: 1 addition & 0 deletions app/components/_index.sass
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@import "shares/invite_user_form_component"
@import "work_packages/details/tab_component"
@import "work_packages/progress/modal_body_component"
@import "work_packages/hover_card_component"
@import "work_packages/split_view_component"
@import "open_project/common/attribute_component"
@import "open_project/common/submenu_component"
Expand Down
11 changes: 11 additions & 0 deletions app/components/work_packages/highlighted_date_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<%=
if @start_date == @due_date
render(Primer::Beta::Text.new(**text_arguments, classes: date_classes(@start_date))) { parsed_date(@start_date) }
else
component_wrapper do
concat(render(Primer::Beta::Text.new(**text_arguments)) { parsed_date(@start_date) })
concat(render(Primer::Beta::Text.new(**text_arguments)) { " - " }) if @due_date.present?
concat(render(Primer::Beta::Text.new(**text_arguments, classes: date_classes(@due_date))) { parsed_date(@due_date) })
end
end
%>
40 changes: 40 additions & 0 deletions app/components/work_packages/highlighted_date_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

class WorkPackages::HighlightedDateComponent < ApplicationComponent
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable

def initialize(work_package:)
super

@work_package = work_package
@start_date = work_package.start_date
@due_date = work_package.due_date
end

def parsed_date(date)
return if date.nil?

date.strftime(I18n.t("date.formats.default"))
end

def date_classes(date)
return if date.nil?

diff = (date - Time.zone.today).to_i
if diff === 0
return "__hl_date_due_today"
elsif diff <= -1
return "__hl_date_overdue"
end

"__hl_date_not_overdue"
end

def text_arguments
{
font_size: :small,
color: :muted
}
end
end
16 changes: 16 additions & 0 deletions app/components/work_packages/highlighted_type_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class WorkPackages::HighlightedTypeComponent < ApplicationComponent
include OpPrimer::ComponentHelpers

def initialize(work_package:, **system_arguments)
super

@type = work_package.type
@system_arguments = system_arguments.merge({ classes: "__hl_inline_type_#{@type.id}" })
end

def call
render(Primer::Beta::Text.new(**@system_arguments)) { @type.name.upcase }
end
end
40 changes: 40 additions & 0 deletions app/components/work_packages/hover_card_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<%=
if @work_package.present?
grid_layout('op-wp-hover-card', tag: :div) do |grid|
grid.with_area(:status, tag: :div, color: :muted) do
render WorkPackages::StatusButtonComponent.new(work_package: @work_package,
user: helpers.current_user,
readonly: true,
button_arguments: { size: :small })
end

grid.with_area(:id, tag: :div, color: :muted) do
render(Primer::Beta::Text.new(font_size: :small)) { "##{@work_package.id}" }
end

grid.with_area(:project, tag: :div, color: :muted) do
render(Primer::Beta::Text.new(font_size: :small)) { "- #{@work_package.project.name}" }
end

grid.with_area(:middleRow, tag: :div) do
concat(render(WorkPackages::HighlightedTypeComponent.new(work_package: @work_package, mr: 1)))
concat(render(Primer::Beta::Text.new(font_weight: :semibold)) { @work_package.subject })
end

if @assignee.present?
grid.with_area(:assignee, tag: :div) do
render(Users::AvatarComponent.new(user: @assignee, show_name: false, size: :medium))
end
end

grid.with_area(:dates, tag: :div) do
render(WorkPackages::HighlightedDateComponent.new(work_package: @work_package))
end
end
else
render Primer::Beta::Blankslate.new(border: false, narrow: true) do |component|
component.with_visual_icon(icon: "x-circle")
component.with_heading(tag: :h3).with_content(I18n.t("api_v3.errors.not_found.work_package"))
end
end
%>
13 changes: 13 additions & 0 deletions app/components/work_packages/hover_card_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

class WorkPackages::HoverCardComponent < ApplicationComponent
include OpPrimer::ComponentHelpers

def initialize(id:)
super

@id = id
@work_package = WorkPackage.visible.find_by(id:)
@assignee = @work_package.present? ? @work_package.assigned_to : nil
end
end
15 changes: 15 additions & 0 deletions app/components/work_packages/hover_card_component.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.op-wp-hover-card
display: grid
align-items: center
grid-template-columns: max-content max-content max-content auto 1fr
grid-template-rows: max-content 1fr auto
grid-row-gap: 5px
grid-column-gap: 5px
grid-template-areas: "status status id project project" "middleRow middleRow middleRow middleRow middleRow" "assignee assignee dates dates dates"
overflow: hidden

&--middleRow
align-self: flex-start

&--dates
justify-self: flex-end
24 changes: 24 additions & 0 deletions app/components/work_packages/status_button_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<%=
if @readonly
render(Primer::Beta::Button.new(**button_arguments)) do |button|
button.with_leading_visual_icon(icon: "lock") if readonly?
@status.name
end
else
render(Primer::Alpha::ActionMenu.new(**@menu_arguments)) do |menu|
menu.with_show_button(**button_arguments) do |button|
button.with_trailing_action_icon(icon: "triangle-down")
button.with_leading_visual_icon(icon: "lock") if readonly?
@status.name
end

@items.each do |item|
menu.with_item(label: item.name,
content_arguments: { classes: "__hl_inline_status_#{item.id}",
align_items: :center }) do |menu_item|
menu_item.with_trailing_visual_icon(icon: :lock) if item.is_readonly?
end
end
end
end
%>
45 changes: 45 additions & 0 deletions app/components/work_packages/status_button_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

class WorkPackages::StatusButtonComponent < ApplicationComponent
include OpPrimer::ComponentHelpers

def initialize(work_package:, user:, readonly: false, button_arguments: {}, menu_arguments: {})
super

@work_package = work_package
@user = user
@status = work_package.status
@project = work_package.project

@readonly = readonly
@menu_arguments = menu_arguments
@button_arguments = button_arguments.merge({ classes: "__hl_background_status_#{@status.id}" })

@items = available_statusses
end

def button_title
I18n.t("js.label_edit_status")
end

def disabled?
[email protected]_in_project?(:edit_work_packages, @project)
end

def readonly?
@status.is_readonly?
end

def button_arguments
{ title: button_title,
disabled: disabled?,
aria: {
label: button_title
} }.deep_merge(@button_arguments)
end

def available_statusses
WorkPackages::UpdateContract.new(@work_package, @user)
.assignable_statuses
end
end
37 changes: 37 additions & 0 deletions app/controllers/work_packages/hover_card_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#-- 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 WorkPackages
class HoverCardController < ApplicationController
before_action :load_and_authorize_in_optional_project

def show
@id = params[:id]
render layout: nil
end
end
end
14 changes: 0 additions & 14 deletions app/helpers/work_packages_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,20 +171,6 @@ def send_notification_option(checked = false)
end
end

# Returns a string of css classes that apply to the issue
def work_package_css_classes(work_package)
s = "work_package preview-trigger".html_safe
s << " status-#{work_package.status.position}" if work_package.status
s << " priority-#{work_package.priority.position}" if work_package.priority
s << " closed" if work_package.closed?
s << " overdue" if work_package.overdue?
s << " child" if work_package.child?
s << " parent" unless work_package.leaf?
s << " created-by-me" if User.current.logged? && work_package.author_id == User.current.id
s << " assigned-to-me" if User.current.logged? && work_package.assigned_to_id == User.current.id
s
end

def work_package_associations_to_address(associated)
ret = "".html_safe

Expand Down
3 changes: 3 additions & 0 deletions app/views/work_packages/hover_card/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<turbo-frame id="op-hover-card-body">
<%= render WorkPackages::HoverCardComponent.new(id: @id) %>
</turbo-frame>
3 changes: 2 additions & 1 deletion config/initializers/permissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@
work_packages: %i[show index],
work_packages_api: [:get],
"work_packages/reports": %i[report report_details],
"work_packages/menus": %i[show]
"work_packages/menus": %i[show],
"work_packages/hover_card": %i[show]
},
permissible_on: %i[work_package project],
contract_actions: { work_packages: %i[read] }
Expand Down
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,8 @@
resources :work_packages, only: [:index] do
concerns :shareable

get "hover_card" => "work_packages/hover_card#show", on: :member

# move bulk of wps
get "move/new" => "work_packages/moves#new", on: :collection, as: "new_move"
post "move" => "work_packages/moves#create", on: :collection, as: "move"
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { OpenprojectDashboardsModule } from 'core-app/features/dashboards/openpr
import {
OpenprojectWorkPackageGraphsModule,
} from 'core-app/shared/components/work-package-graphs/openproject-work-package-graphs.module';
import { PreviewTriggerService } from 'core-app/core/setup/globals/global-listeners/preview-trigger.service';
import { HoverCardTriggerService } from 'core-app/core/setup/globals/global-listeners/hover-card-trigger.service';
import { OpenprojectOverviewModule } from 'core-app/features/overview/openproject-overview.module';
import { OpenprojectMyPageModule } from 'core-app/features/my-page/openproject-my-page.module';
import { OpenprojectProjectsModule } from 'core-app/features/projects/openproject-projects.module';
Expand All @@ -77,8 +77,8 @@ import {
PasswordConfirmationModalComponent,
} from 'core-app/shared/components/modals/request-for-confirmation/password-confirmation.modal';
import {
WpPreviewModalComponent,
} from 'core-app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal';
HoverCardComponent,
} from 'core-app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal';
import {
OpHeaderProjectSelectComponent,
} from 'core-app/shared/components/header-project-select/header-project-select.component';
Expand Down Expand Up @@ -240,7 +240,7 @@ import { SpotSwitchComponent } from 'core-app/spot/components/switch/switch.comp

export function initializeServices(injector:Injector) {
return () => {
const PreviewTrigger = injector.get(PreviewTriggerService);
const PreviewTrigger = injector.get(HoverCardTriggerService);
const topMenuService = injector.get(TopMenuService);
const keyboardShortcuts = injector.get(KeyboardShortcutService);
// Conditionally add the Revit Add-In settings button
Expand Down Expand Up @@ -370,7 +370,7 @@ export function initializeServices(injector:Injector) {
ConfirmDialogModalComponent,
DynamicContentModalComponent,
PasswordConfirmationModalComponent,
WpPreviewModalComponent,
HoverCardComponent,

// Main menu
MainMenuResizerComponent,
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/app/core/path-helper/path-helper.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,10 @@ export class PathHelperService {
return `${this.workPackagePath(workPackageId)}/shares`;
}

public workPackageHoverCardPath(workPackageId:string|number) {
return `${this.workPackagePath(workPackageId)}/hover_card`;
}

public workPackageProgressModalPath(workPackageId:string|number) {
if (workPackageId === 'new') {
return `${this.workPackagePath(workPackageId)}/progress/new`;
Expand Down
Loading
Loading