Skip to content

Commit 9446f6a

Browse files
Merge pull request #17767 from opf/feature/meeting-index-organization
Better structure upcoming meetings by organising them into named date sections
2 parents 06f81ec + bcd7d95 commit 9446f6a

File tree

17 files changed

+557
-57
lines changed

17 files changed

+557
-57
lines changed

app/helpers/pagination_helper.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
require "will_paginate"
3030

3131
module PaginationHelper
32+
SHOW_MORE_DEFAULT_LIMIT = 5
33+
SHOW_MORE_DEFAULT_INCREMENT = 20
34+
SHOW_MORE_MAX_LIMIT = 1000
35+
3236
def pagination_links_full(paginator, options = {})
3337
return unless paginator.total_entries > 0
3438

@@ -151,6 +155,24 @@ def per_page_param(options = params)
151155
end
152156
end
153157

158+
##
159+
# For "Show more" paginated links, we want to load an initial number of items (defaulting to 5)
160+
# unless a higher number is provided. These values do not correspond to the per_page_options
161+
def show_more_limit_param(options = params, initial_limit: SHOW_MORE_DEFAULT_LIMIT)
162+
limit = options[:limit].to_i
163+
if limit.zero?
164+
initial_limit
165+
else
166+
[limit, SHOW_MORE_MAX_LIMIT].min
167+
end
168+
end
169+
170+
##
171+
# Paginate an AR relation for the "show more" pagination functionality
172+
def show_more_pagination(paginator, options = params)
173+
paginator.paginate(page: 1, per_page: show_more_limit_param(options))
174+
end
175+
154176
class LinkRenderer < ::WillPaginate::ActionView::LinkRenderer
155177
def to_html
156178
pagination.inject("") do |html, item|

lib_static/redmine/i18n.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ def self.all_languages
4545
.sort
4646
end
4747

48+
def self.start_of_week
49+
case Setting.start_of_week.to_i
50+
when 1
51+
:monday
52+
when 7
53+
:sunday
54+
when 6
55+
:saturday
56+
else
57+
Date.beginning_of_week
58+
end
59+
end
60+
4861
def self.valid_languages
4962
all_languages & (Setting.available_languages + [Setting.default_language])
5063
end

modules/meeting/app/components/meetings/table_component.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,23 @@ def sortable?
4444
false
4545
end
4646

47+
def paginated?
48+
false
49+
end
50+
51+
def has_footer?
52+
model.is_a?(ActiveRecord::Relation) &&
53+
(model.total_entries > model.size)
54+
end
55+
56+
def footer
57+
render Meetings::TableFooterComponent.new(
58+
upcoming: options[:upcoming],
59+
total: model.total_entries,
60+
count: model.size
61+
)
62+
end
63+
4764
def has_actions?
4865
true
4966
end
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<%#-- copyright
2+
OpenProject is an open source project management software.
3+
Copyright (C) the OpenProject GmbH
4+
5+
This program is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU General Public License version 3.
7+
8+
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
9+
Copyright (C) 2006-2013 Jean-Philippe Lang
10+
Copyright (C) 2010-2013 the ChiliProject Team
11+
12+
This program is free software; you can redistribute it and/or
13+
modify it under the terms of the GNU General Public License
14+
as published by the Free Software Foundation; either version 2
15+
of the License, or (at your option) any later version.
16+
17+
This program is distributed in the hope that it will be useful,
18+
but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
GNU General Public License for more details.
21+
22+
You should have received a copy of the GNU General Public License
23+
along with this program; if not, write to the Free Software
24+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25+
26+
See COPYRIGHT and LICENSE files for more details.
27+
28+
++#%>
29+
30+
<%=
31+
component_wrapper do
32+
flex_layout(justify_content: :space_between) do |flex|
33+
flex.with_column(classes: "ellipsis") do
34+
render(Primer::BaseComponent.new(tag: :span, color: :muted)) do
35+
concat render(Primer::Beta::Octicon.new(icon: :iterations, mr: 1, ml: 1))
36+
concat render(Primer::Beta::Text.new(font_weight: :bold)) { label }
37+
end
38+
end
39+
flex.with_column do
40+
render(
41+
Primer::Beta::Button.new(
42+
scheme: :link,
43+
size: :medium,
44+
tag: :a,
45+
href: url_for(limit: next_count, upcoming:),
46+
data: {
47+
keep_scroll_position_target: "triggerButton"
48+
}
49+
)
50+
) do
51+
I18n.t(:label_recurring_meeting_show_more)
52+
end
53+
end
54+
end
55+
end
56+
%>

modules/meeting/app/components/recurring_meetings/show_component.rb renamed to modules/meeting/app/components/meetings/table_footer_component.rb

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,31 @@
2626
# See COPYRIGHT and LICENSE files for more details.
2727
#++
2828

29-
module RecurringMeetings
30-
class ShowComponent < ApplicationComponent
29+
module Meetings
30+
class TableFooterComponent < ApplicationComponent
3131
include ApplicationHelper
32+
include OpTurbo::Streamable
3233
include OpPrimer::ComponentHelpers
3334

34-
def initialize(meeting:, project:)
35+
attr_reader :total, :count, :upcoming
36+
37+
def initialize(count:, total:, upcoming:)
3538
super
3639

37-
@meeting = meeting
38-
@project = project
40+
@count = count
41+
@total = total
42+
@upcoming = upcoming
43+
end
44+
45+
def label
46+
I18n.t(:label_meeting_more, count: total - count)
47+
end
48+
49+
def next_count
50+
[
51+
total,
52+
count + PaginationHelper::SHOW_MORE_DEFAULT_INCREMENT
53+
].min
3954
end
4055
end
4156
end

modules/meeting/app/components/recurring_meetings/show_component.html.erb

Whitespace-only changes.

modules/meeting/app/controllers/meetings_controller.rb

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ class MeetingsController < ApplicationController
5858
menu_item :new_meeting, only: %i[new create]
5959

6060
def index
61-
@query = load_query
62-
@meetings = load_meetings(@query)
61+
load_meetings
6362

6463
render "index",
6564
locals: { menu_name: project_or_global_menu }
@@ -353,10 +352,43 @@ def apply_default_filter_if_none_given(query)
353352
query.where("invited_user_id", "=", [User.current.id.to_s])
354353
end
355354

356-
def load_meetings(query)
357-
query
358-
.results
359-
.paginate(page: page_param, per_page: per_page_param)
355+
def load_meetings
356+
@query = load_query
357+
358+
# We group meetings into individual groups, but only for upcoming meetings
359+
if params[:upcoming] == "false"
360+
@meetings = show_more_pagination(@query.results)
361+
else
362+
@grouped_meetings = group_meetings(@query.results)
363+
end
364+
end
365+
366+
def group_meetings(all_meetings) # rubocop:disable Metrics/AbcSize
367+
next_week = Time.current.next_occurring(Redmine::I18n.start_of_week)
368+
groups = Hash.new { |h, k| h[k] = [] }
369+
groups[:later] = show_more_pagination(all_meetings
370+
.where(start_time: next_week..)
371+
.order(start_time: :asc))
372+
373+
all_meetings
374+
.where(start_time: ...next_week)
375+
.order(start_time: :asc)
376+
.each do |meeting|
377+
start_date = meeting.start_time.to_date
378+
379+
group_key =
380+
if start_date == Time.zone.today
381+
:today
382+
elsif start_date == Time.zone.tomorrow
383+
:tomorrow
384+
else
385+
:this_week
386+
end
387+
388+
groups[group_key] << meeting
389+
end
390+
391+
groups
360392
end
361393

362394
def build_meeting

modules/meeting/app/controllers/recurring_meetings_controller.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ class RecurringMeetingsController < ApplicationController
2323
menu_item :meetings
2424

2525
def index
26-
@recurring_meetings =
26+
results =
2727
if @project
2828
RecurringMeeting.visible.where(project_id: @project.id)
2929
else
3030
RecurringMeeting.visible
3131
end
3232

33+
@recurring_meetings = show_more_pagination(results)
34+
3335
respond_to do |format|
3436
format.html do
3537
render :index, locals: { menu_name: project_or_global_menu }
@@ -51,11 +53,12 @@ def show # rubocop:disable Metrics/AbcSize
5153
params[:count].to_i + 5
5254
end
5355

54-
if @direction == "past"
55-
@meetings = @recurring_meeting.scheduled_instances(upcoming: false).limit(@count)
56-
else
57-
@meetings, @planned_meetings = upcoming_meetings(count: @count)
58-
end
56+
57+
if @direction == "past"
58+
@meetings = @recurring_meeting.scheduled_instances(upcoming: false).limit(@count)
59+
else
60+
@meetings, @planned_meetings =upcoming_meetings(count: @count)
61+
end
5962

6063
respond_to do |format|
6164
format.html do

modules/meeting/app/menus/meetings/menu.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def involvement_sidebar_menu_items
9595
menu_item(title: I18n.t(:label_invitations),
9696
query_params: { filters: invitation_filter, sort: "start_time" }),
9797
menu_item(title: I18n.t(:label_attended),
98-
query_params: { filters: attendee_filter }),
98+
query_params: { filters: attendee_filter, upcoming: false }),
9999
menu_item(title: I18n.t(:label_created_by_me),
100100
query_params: { filters: author_filter })
101101
]

modules/meeting/app/models/queries/meetings/filters/time_filter.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ def past?
4545

4646
def where
4747
if past?
48-
'"meetings"."start_time" < NOW()'
48+
['"meetings"."start_time" < ?', Time.current]
4949
else
50-
'"meetings"."start_time" + "meetings"."duration" * interval \'1 hour\' > NOW()'
50+
['"meetings"."start_time" + "meetings"."duration" * interval \'1 hour\' >= ?', Time.current]
5151
end
5252
end
5353

0 commit comments

Comments
 (0)