Skip to content

Commit

Permalink
Merge pull request #17369 from opf/feature/59883-pagination-for-recur…
Browse files Browse the repository at this point in the history
…ring-meetings
  • Loading branch information
oliverguenther authored Dec 9, 2024
2 parents 339a2b5 + b0f2dda commit 8141ce3
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 14 deletions.
6 changes: 6 additions & 0 deletions app/components/op_primer/border_box_table_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ See COPYRIGHT and LICENSE files for more details.
end
end
end

if has_footer?
component.with_footer(classes: grid_class, color: :muted) do
footer
end
end
end
%>

Expand Down
8 changes: 8 additions & 0 deletions app/components/op_primer/border_box_table_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ def has_actions?
false
end

def has_footer?
false
end

def sortable?
false
end
Expand Down Expand Up @@ -133,5 +137,9 @@ def blank_description
def blank_icon
nil
end

def footer
raise ArgumentError, "Need to provide footer content"
end
end
end
11 changes: 11 additions & 0 deletions lookbook/docs/patterns/12-tables.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,19 @@ main_column :title
```
Note: Ideally, one one main column will be present for each table.

#### Footer

Set `has_footer?` to true to add a footer to the table, defining the component/content to be rendered with the `footer` method.

```ruby
def has_footer?
true
end

def footer
render CustomFooterComponent.new(...)
end
```

## Best practices

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<%#-- 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.
++#%>

<%=
component_wrapper do
flex_layout(justify_content: :space_between) do |flex|
flex.with_column(classes: "ellipsis") do
render(Primer::BaseComponent.new(tag: :span, color: :muted)) do
concat render(Primer::Beta::Octicon.new(icon: :iterations, mr: 1, ml: 1))
concat render(Primer::Beta::Text.new(font_weight: :bold)) { label }
end
end

flex.with_column do
render(
Primer::Beta::Button.new(
scheme: :link,
size: :medium,
tag: :a,
href: polymorphic_path([@project, @meeting], count: @current_count, direction: @direction)
)
) do |_c|
I18n.t(:label_recurring_meeting_show_more)
end
end
end
end
%>


Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#-- 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 RecurringMeetings
class FooterComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers

def initialize(meeting:, project:, count:, direction:, max_count:)
super

@meeting = meeting
@project = project
@current_count = count
@direction = direction
@max_count = max_count
end

def label
count = @max_count - @current_count
label_suffix = count == 1 ? "_singular" : ""

if @direction == "past"
I18n.t("label_recurring_meeting_more_past#{label_suffix}", count:)
else
I18n.t("label_recurring_meeting_more#{label_suffix}", count:, schedule: @meeting.schedule_in_words)
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,30 @@

module RecurringMeetings
class TableComponent < ::OpPrimer::BorderBoxTableComponent
options :current_project
options :current_project, :count, :direction, :max_count

columns :start_time, :relative_time, :last_edited, :status, :create

def has_actions?
true
end

def has_footer?
return false unless recurring_meeting

options[:max_count] - count > 0
end

def footer
render RecurringMeetings::FooterComponent.new(
meeting: recurring_meeting,
project: options[:current_project],
direction: options[:direction],
max_count: options[:max_count],
count:
)
end

def header_args(column)
if column == :title
{ style: "grid-column: span 2" }
Expand All @@ -63,5 +79,15 @@ def headers
def columns
@columns ||= headers.map(&:first)
end

def recurring_meeting
return if model.blank?

@recurring_meeting ||= model.first.recurring_meeting
end

def count
@count ||= [options[:count], rows.count].max
end
end
end
24 changes: 12 additions & 12 deletions modules/meeting/app/controllers/recurring_meetings_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class RecurringMeetingsController < ApplicationController
include RecurringMeetingsHelper
include Layout
include PaginationHelper
include OpTurbo::ComponentStream
Expand Down Expand Up @@ -36,17 +37,16 @@ def new
@recurring_meeting = RecurringMeeting.new(project: @project)
end

def show # rubocop:disable Metrics/AbcSize
def show
@direction = params[:direction]
if params[:direction] == "past"
@meetings = @recurring_meeting
.scheduled_instances(upcoming: false)
.page(page_param)
.per_page(per_page_param)
else
@meetings = upcoming_meetings
@total_count = @recurring_meeting.remaining_occurrences.count - @meetings.count
end
@max_count = max_count
@count = [(params[:count].to_i + 5), @max_count].min

@meetings = if @direction == "past"
@recurring_meeting.scheduled_instances(upcoming: false).limit(@count)
else
upcoming_meetings(count: @count)
end

respond_to do |format|
format.html do
Expand Down Expand Up @@ -180,13 +180,13 @@ def download_ics

private

def upcoming_meetings
def upcoming_meetings(count:)
meetings = @recurring_meeting
.scheduled_instances(upcoming: true)
.index_by(&:start_time)

merged = @recurring_meeting
.scheduled_occurrences(limit: 5)
.scheduled_occurrences(limit: count)
.map do |start_time|
meetings.delete(start_time) || scheduled_meeting(start_time)
end
Expand Down
37 changes: 37 additions & 0 deletions modules/meeting/app/helpers/recurring_meetings_helper.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 RecurringMeetingsHelper
def max_count
if @direction == "past"
@recurring_meeting.scheduled_instances(upcoming: false).count
else
@recurring_meeting.remaining_occurrences.count
end
end
end
3 changes: 2 additions & 1 deletion modules/meeting/app/views/recurring_meetings/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>

<% html_title t(:label_recurring_meeting_plural) %>

<%= render(RecurringMeetings::ShowPageHeaderComponent.new(project: @project, meeting: @recurring_meeting)) %>
Expand All @@ -34,5 +35,5 @@ See COPYRIGHT and LICENSE files for more details.
<% if @recurring_meeting.nil? -%>
<%= no_results_box %>
<% else -%>
<%= render RecurringMeetings::TableComponent.new(rows: @meetings, current_project: @project) %>
<%= render RecurringMeetings::TableComponent.new(rows: @meetings, current_project: @project, count: @count, direction: @direction, max_count: @max_count) %>
<% end -%>
5 changes: 5 additions & 0 deletions modules/meeting/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ en:
Any meeting information not in the template will be lost.
Do you want to continue?
label_recurring_meeting_restore: "Restore this occurrence"
label_recurring_meeting_more: "There are %{count} more scheduled meetings (%{schedule})."
label_recurring_meeting_more_singular: "There is %{count} more scheduled meeting (%{schedule})."
label_recurring_meeting_more_past: "There are %{count} more past meetings."
label_recurring_meeting_more_past_singular: "There is %{count} more past meeting."
label_recurring_meeting_show_more: "Show more"
label_recurring_meeting_series_edit: "Edit meeting series"
label_recurring_meeting_series_delete: "Delete meeting series"
label_my_meetings: "My meetings"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,32 @@
expect(response).to have_http_status(:not_found)
end
end

describe "Show more" do
context "when there are more than 5 scheduled instances" do
it "shows the footer" do
get recurring_meeting_path(recurring_meeting)

expect(page).to have_css("#recurring-meetings-footer-component")
end
end

context "when there are 5 or fewer scheduled instances" do
let(:recurring_meeting) do
create :recurring_meeting,
project:,
author: user,
start_time: Time.zone.today + 1.day,
frequency: "daily",
end_after: "iterations",
iterations: 5
end

it "shows no footer" do
get recurring_meeting_path(recurring_meeting)

expect(page).to have_no_css("#recurring-meetings-footer-component")
end
end
end
end

0 comments on commit 8141ce3

Please sign in to comment.