Skip to content

Commit

Permalink
Merge pull request #16885 from opf/feature/55581-create-generic-hover…
Browse files Browse the repository at this point in the history
…-card

[58242] Generalize the wp preview modal to be reused for users
  • Loading branch information
oliverguenther authored Oct 9, 2024
2 parents 2359138 + ed667cc commit a834a6f
Show file tree
Hide file tree
Showing 35 changed files with 430 additions and 238 deletions.
Binary file added app/assets/images/lookbook/hover_card.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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?
!@user.allowed_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

0 comments on commit a834a6f

Please sign in to comment.