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

Code maintenance/53379 separate filtering from projections in queries #15662

Draft
wants to merge 28 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
98c86b6
turn factory project independent - move ProjectQuery services and con…
ulferts Jun 28, 2024
79b75a4
evaluate query selects instead of statically adding expensive calcula…
ulferts Mar 8, 2024
0b2c652
move visible into own cte
ulferts Mar 8, 2024
b25677d
fix order by latest_activity_at
ulferts Mar 8, 2024
8606952
start splitting up table components into scope and query based
ulferts Mar 8, 2024
a6140a7
extract ColumnHeaderComponent from Table - project specific for now
ulferts Mar 12, 2024
a96b2b6
linting
ulferts May 24, 2024
05aaf68
adapt spec expectations
ulferts May 24, 2024
3feb8c8
rename hierarchy in default column
ulferts May 28, 2024
cb505d4
add method from default table component
ulferts May 28, 2024
60f3a70
robustness when not ordering at all
ulferts May 28, 2024
90c1121
separate filtering from selection
ulferts May 30, 2024
9150d36
extract method
ulferts May 30, 2024
9b52af9
apply separation to queries even without selection
ulferts May 30, 2024
3614a87
apply direction check to base order
ulferts May 30, 2024
242e19a
adapt to CTE usage
ulferts May 30, 2024
96afe16
start on using apply_to for selects
ulferts Jun 26, 2024
e9d61bb
fix group order in case a user is not a group member
ulferts Jun 26, 2024
917ff05
rewrite sql expectations
ulferts Jun 26, 2024
ed2db48
apply query based column to meetings
ulferts Jun 28, 2024
303dc36
make use of the default query loading for meetings
ulferts Jun 28, 2024
10f9836
linting
ulferts Jul 9, 2024
b9ace45
fix contract loading
ulferts Jul 12, 2024
86f2b60
fix selecting meeting menu item
ulferts Jul 12, 2024
033f092
remove unnecessary rubocop statements
ulferts Jul 12, 2024
a1ce607
extract query based table component
ulferts Jul 12, 2024
4322325
move column header rendering into shared component
ulferts Jul 15, 2024
64bc47e
reintroduce table id as it is referenced in too many specs
ulferts Aug 15, 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
92 changes: 92 additions & 0 deletions app/components/projects/column_header_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-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.
# ++

class Projects::ColumnHeaderComponent < Tables::ColumnHeaderComponent
def column_caption
if lft_column?(column)
helpers.op_icon("icon-hierarchy")
elsif favored_column?(column)
render(Primer::Beta::Octicon.new(icon: "star-fill", color: :subtle, ml: 2, "aria-label": I18n.t(:label_favorite)))
else
super
end
end

def sort_class(column)
order = order_string(column)

order.nil? ? nil : "sort #{order}"
end

def header_options(column)
options = super

if lft_column?(column)
options[:id] = "project-table--hierarchy-header"
end

options
end

def sort_header_outer_classes(column)
if lft_column?(column)
"generic-table--sort-header-outer_no-highlighting"
elsif favored_column?(column)
"generic-table--header_centered generic-table--header_no-min-width"
else
super
end
end

def sort_link_title(column)
if lft_column?(column)
t(:label_sort_by, value: %("#{t(:label_project_hierarchy)}"))
else
super
end
end

def sortable_column?(column)
(lft_column?(column) && !sorted_by_lft?) ||
(!lft_column?(column) && super)
end

def sorted_by_lft?
first_order_by&.attribute == :lft
end

def lft_column?(column)
column.attribute == :lft
end

def favored_column?(column)
column.attribute == :favored
end
end
4 changes: 2 additions & 2 deletions app/components/projects/row_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def level
model.last
end

# Hierarchy cell is just a placeholder
def hierarchy
# lft (hierarchy) cell is just a placeholder
def lft
""
end

Expand Down
108 changes: 0 additions & 108 deletions app/components/projects/table_component.html.erb

This file was deleted.

113 changes: 15 additions & 98 deletions app/components/projects/table_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,114 +29,31 @@
#++

module Projects
class TableComponent < ::TableComponent
options :params # We read collapsed state from params
options :current_user # adds this option to those of the base class
options :query

def initialize(**)
super(rows: [], **)
end

def before_render
@model = projects(query)
super
end

def initial_sort
%i[lft asc]
end

def table_id
"project-table"
end

def container_class
"generic-table--container_visible-overflow generic-table--container_height-100"
end

##
# The project sort by is handled differently
def build_sort_header(column, options)
helpers.projects_sort_header_tag(column, **options, param: :json)
end

# We don't return the project row
# but the [project, level] array from the helper
def rows
@rows ||= begin
projects_enumerator = ->(model) { to_enum(:projects_with_levels_order_sensitive, model).to_a }
instance_exec(model, &projects_enumerator)
end
end

def initialize_sorted_model
helpers.sort_clear

orders = query.orders.select(&:valid?).map { |o| [o.attribute.to_s, o.direction.to_s] }
helpers.sort_init orders
helpers.sort_update orders.map(&:first)
end

def paginated?
true
end

def pagination_options
default_pagination_options.merge(optional_pagination_options)
end

def default_pagination_options
{ allowed_params: %i[query_id filters columns sortBy] }
end

def optional_pagination_options
{}
end

def deactivate_class_on_lft_sort
if sorted_by_lft?
"spot-link_inactive"
end
end

def href_only_when_not_sort_lft
unless sorted_by_lft?
projects_path(
sortBy: JSON.dump([%w[lft asc]]),
**helpers.projects_query_params.slice(*helpers.projects_query_param_names_for_sort)
)
end
end

def order_options(select)
{
caption: select.caption
}
end

def sortable_column?(select)
sortable? && query.known_order?(select.attribute)
end
class TableComponent < Tables::QueryComponent
self.eager_load = :enabled_modules

def columns
@columns ||= begin
columns = query.selects.reject { |select| select.is_a?(::Queries::Selects::NotExistingSelect) }
columns = super

index = columns.index { |column| column.attribute == :name }
columns.insert(index, ::Queries::Projects::Selects::Default.new(:hierarchy)) if index
columns.insert(index, ::Queries::Projects::Selects::Default.new(:lft)) if index

columns
end
end

def projects(query)
query
.results
.with_required_storage
.with_latest_activity
.includes(:custom_values, :enabled_modules)
.paginate(page: helpers.page_param(params), per_page: helpers.per_page_param(params))
def highlight_column?(column)
return false if column.attribute == :lft

super
end

# We don't return the project row
# but the [project, level] array from the helper
def render_rows
render(Projects::RowComponent.with_collection(to_enum(:projects_with_levels_order_sensitive, rows).to_a,
table: self))
end

def projects_with_levels_order_sensitive(projects, &)
Expand Down
8 changes: 6 additions & 2 deletions app/components/row_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,19 @@ def row
end

def column_value(column)
send(column)
if column.respond_to?(:attribute)
send(column.attribute)
else
send(column)
end
end

def column_css_class(column)
column_css_classes[column]
end

def column_css_classes
@column_css_classes ||= columns.to_h { |name| [name, name] }
@column_css_classes ||= columns.index_with { |column| column.respond_to?(:attribute) ? column.attribute : column }
end

def button_links
Expand Down
2 changes: 1 addition & 1 deletion app/components/table_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def add_column(name)
end

def row_class
mod = name.split("::")[0..-2].join("::").presence || "Table"
mod = name.deconstantize

"#{mod}::RowComponent".constantize
rescue NameError
Expand Down
1 change: 1 addition & 0 deletions app/components/tables/column_header_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= column_header %>
Loading
Loading