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

Events calendar view #876

Merged
merged 27 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cd4b40a
simple calendar setup
DaanVanVugt Aug 1, 2023
f4a11ec
ajax loading of calendar
DaanVanVugt Aug 1, 2023
23b8e7d
styling of calendar view, separating out long events
DaanVanVugt Aug 1, 2023
50e4cb2
add calendar html page
DaanVanVugt Aug 3, 2023
dd953ca
add basic calendar test
DaanVanVugt Aug 3, 2023
634bf97
allow specifying max event length for showing in calendar
DaanVanVugt Aug 3, 2023
f2f6d69
remove unused view
DaanVanVugt Aug 3, 2023
c00d334
update styling of calendar month selection
DaanVanVugt Aug 25, 2023
2e7fe27
update long event styling
DaanVanVugt Aug 25, 2023
420420b
Merge remote-tracking branch 'tess/master' into feature/simple_calendar
DaanVanVugt Aug 25, 2023
09b5672
remove extra calendar template
DaanVanVugt Aug 25, 2023
ad854db
fix deeplinking of calendar view
DaanVanVugt Aug 25, 2023
d5cfd6f
allow unauthorized users to see :calendar actions
DaanVanVugt Aug 25, 2023
da0d79e
history for calendar
DaanVanVugt Aug 25, 2023
8a77edd
fixes for events without start or end
DaanVanVugt Aug 25, 2023
ea7eeb1
fix test for js calendar inclusion
DaanVanVugt Aug 25, 2023
cdce641
limit events fetched for calendar to month requested
DaanVanVugt Aug 25, 2023
f15b412
simplify events controller
DaanVanVugt Aug 25, 2023
7193c97
smaller text with margin between
mikesndrs Aug 28, 2023
41d36ce
warning to pick range of dates in case of database bug
mikesndrs Aug 28, 2023
ff5e75b
slightly more margin
mikesndrs Aug 28, 2023
509a577
double query
mikesndrs Aug 30, 2023
750492c
test
mikesndrs Aug 30, 2023
711868b
merge origin
mikesndrs Oct 2, 2023
e6c1a85
fix test
mikesndrs Oct 2, 2023
54c25ca
undo calendar_long_events changes since it caused fauled tests
mikesndrs Oct 2, 2023
7e7d473
neatly_printed_date_range
mikesndrs Oct 3, 2023
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
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ gem 'sass-rails'
gem 'sassc-rails'
gem 'sidekiq'
gem 'sidekiq-status'
gem 'simple_calendar', '~> 2.4'
gem 'simple_form'
gem 'simple_token_authentication'
gem 'sitemap-parser'
Expand Down
5 changes: 4 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,8 @@ GEM
sidekiq-status (3.0.3)
chronic_duration
sidekiq (>= 6.0, < 8)
simple_calendar (2.4.3)
rails (>= 3.0)
simple_form (5.2.0)
actionpack (>= 5.2)
activemodel (>= 5.2)
Expand Down Expand Up @@ -819,6 +821,7 @@ DEPENDENCIES
sassc-rails
sidekiq
sidekiq-status
simple_calendar (~> 2.4)
simple_form
simple_token_authentication
simplecov
Expand All @@ -840,4 +843,4 @@ DEPENDENCIES
will_paginate

BUNDLED WITH
2.4.19
2.4.20
44 changes: 43 additions & 1 deletion app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,14 @@ function reposition_tiles(container, tileClass){
});
}

document.addEventListener("turbolinks:load", function() {
// Perform an ajax request to load the calendar and replace the contents
window.loadCalendar = function(url) {
req = $.ajax(url);
req.done((res) => eval(res));
return true;
}

document.addEventListener("turbolinks:load", function(e) {
// Show the tab associated with the window location hash (e.g. "#packages")
if (window.location.hash) {
var tab = $('ul.nav a[href="' + window.location.hash + '"]');
Expand Down Expand Up @@ -116,6 +123,41 @@ document.addEventListener("turbolinks:load", function() {
}
});

// Load event calendar when tab is shown for the first time
$('.nav li a[data-calendar]').on("show.bs.tab", function(e) {
data = e.target.dataset
// calendar has already been loaded, only perform the filter sidebar url fragment replacing
if (!data.loaded) {
let url = data.calendar;
if (date = localStorage.getItem('calendar_start_date')) {
// Only use the start date in localstorage if it is in the future
if (Date.parse(date) > Date.now() - 60*60*24*30*1000) url += '&start_date=' + date
}

loadCalendar(url);
// avoid loading again on the second click
data.loaded = true;
}
});

// after switching tabs automatically update the url fragment
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
addTabToFilters(e.target.href.split('#').pop());
// and reposition masonry tiles
reposition_tiles('masonry', 'masonry-brick');
});

// Manually trigger bootstrap tab history (we should probably remove the dependency and reimplement in a turbolink-compatible way)
// Specialised form of https://github.com/mnarayan01/bootstrap-tab-history/blob/master/vendor/assets/javascripts/bootstrap-tab-history.js
// go through the tabs to find one which has ah ref identical to the page we have just moved to and show it
$('[data-toggle="tab"]').each(function() {
if (("#" + this.href.split("#").pop()) === window.location.hash) {
if (!("active" in this.parentElement.classList)) {
$(this).tab('show');
}
}
})

// Masonry
$(".nav-tabs a").on("shown.bs.tab", function(e) {
reposition_tiles('masonry', 'masonry-brick');
Expand Down
39 changes: 20 additions & 19 deletions app/assets/javascripts/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,32 +42,33 @@ var Map = {
}
}

/* Set all filter links to include the anchor */
var addTabToFilters = function (tab) {
if (tab) {
$(function () {
$('.active-filters a').attr('href', function (_, oldHref) {
oldHref = oldHref.replace(/\#(.*)/g, "#" + tab);
if (oldHref.indexOf('#') == -1)
oldHref += "#" + tab;
return oldHref;
})
$('.nav-item a').attr('href', function (_, oldHref) {
oldHref = oldHref.replace(/\#(.*)/g, "#" + tab);
if (oldHref.indexOf('#') == -1)
oldHref += "#" + tab;
return oldHref;
});
});
}
};

var EventsMap = {
map: null,
init: function () {
// Map on events index
var element = $('[data-role="events-map"]');
if (element.length) {
EventsMap.map = null;
/* Set all filter links to include the anchor */
var addTabToFilters = function (tab) {
if (tab) {
$(function () {
$('.active-filters a').attr('href', function (_, oldHref) {
oldHref = oldHref.replace(/\#(.*)/g, "#" + tab);
if (oldHref.indexOf('#') == -1)
oldHref += "#" + tab;
return oldHref;
})
$('.nav-item a').attr('href', function (_, oldHref) {
oldHref = oldHref.replace(/\#(.*)/g, "#" + tab);
if (oldHref.indexOf('#') == -1)
oldHref += "#" + tab;
return oldHref;
});
});
}
};

var getTab = function () {
var tab = window.location.hash;
Expand Down
18 changes: 18 additions & 0 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
@import "jquery.qtip.min";
@import "select2";
@import "select2-bootstrap-theme";
@import "simple_calendar";

// Sticky but not fixed footer
// http://cbracco.me/css-sticky-footer-effect/
Expand Down Expand Up @@ -984,6 +985,23 @@ div {
display: inline-block
}

/* Calendar inset numbers */
td.day {
position: relative;
}
td.day .day-number {
position: absolute;
bottom: 0px;
right: 6px;
font-size: 1.5em;
color: rgb(186, 186, 186);
}
td.day .calendar-text {
font-size: 12px;
line-height: 14px;
margin-bottom: 12px;
}

.source-log {
max-height: 20pc;
overflow-y: scroll;
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception

# Should allow token authentication for API calls
acts_as_token_authentication_handler_for User, except: [:index, :show, :embed, :check_exists, :handle_error, :count,
acts_as_token_authentication_handler_for User, except: [:index, :show, :embed, :calendar, :check_exists, :handle_error, :count,
:redirect] #only: [:new, :create, :edit, :update, :destroy]

# User auth should be required in the web interface as well; it's here rather than in routes so that it
# doesn't override the token auth, above.
before_action :authenticate_user!, except: [:index, :show, :embed, :check_exists, :handle_error, :count, :redirect]
before_action :authenticate_user!, except: [:index, :show, :embed, :calendar, :check_exists, :handle_error, :count, :redirect]
before_action :set_current_user

# Should prevent forgery errors for JSON posts.
Expand Down
30 changes: 30 additions & 0 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,36 @@ def index
end
end

# GET /events/calendar
# GET /events/calendar.js
def calendar
DaanVanVugt marked this conversation as resolved.
Show resolved Hide resolved
# set a higher limit so we ensure to have enough results to fill a month
params[:per_page] ||= 200
@start_date = Date.parse(params.fetch(:start_date, Date.today.to_s)).beginning_of_month

set_params

# override @facet_params to get only events relevant for the current month view
@facet_params[:running_during] = "#{Date.today.beginning_of_day}/#{@start_date + 1.month}"
fetch_resources
events_set = @events

@facet_params[:running_during] = "#{@start_date}/#{@start_date + 1.month}"
fetch_resources
events_set += @events

Comment on lines +39 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to do 2 solr queries?

Copy link
Contributor

@mikesndrs mikesndrs Oct 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now it is done to make sure that the relevant events after the current day are not left out in favor of the past events if there are a lot of them. Looking for a better solution

@events = events_set.to_set.to_a

# now customize the list by moving all events longer than 3 days into a separate array
@long_events, @events = @events.partition { |e| e.end.nil? || e.start.nil? || e.start + TeSS::Config.site.fetch(:calendar_event_maxlength, 5).to_i.days < e.end }

respond_to do |format|
format.js
format.html
end
end


# GET /events/1
# GET /events/1.json
# GET /events/1.ics
Expand Down
3 changes: 1 addition & 2 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,11 @@ def collapsible_panel(title, id, &block)
end
end

def tab(text, icon, href, disabled: { check: false }, active: false, count: nil, activator: nil)
def tab(text, icon, href, disabled: { check: false }, active: false, count: nil, activator: nil, options: {})
classes = []
classes << 'disabled' if disabled[:check]
classes << 'active' if active || activator&.check_tab(href, !disabled[:check])
content_tag(:li, class: classes.join(' ')) do
options = {}
if disabled[:check]
options['title'] = disabled[:message]
options['data-toggle'] = 'tooltip'
Expand Down
4 changes: 4 additions & 0 deletions app/views/events/calendar.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div id="events-calendar">
<%= render("events/partials/calendar", events: @events) %>
<%= render("events/partials/calendar_long_events", events: @long_events) %>
</div>
4 changes: 4 additions & 0 deletions app/views/events/calendar.js.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
$("#events_calendar").html("<%= escape_javascript render("events/partials/calendar", events: @events) %>");
$("#events_calendar_long_events").html("<%= escape_javascript render("events/partials/calendar_long_events", events: @long_events) %>");
<%# Store the start_date in LocalStorage so we can render the calendar from the correct month %>
localStorage.setItem('calendar_start_date', '<%= @start_date %>');
6 changes: 6 additions & 0 deletions app/views/events/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
<% end %>
<% content_for :display_options do %>
<ul class="nav nav-xs nav-pills index-display-options">
<%# We should consider setting the active status based on the query fragment so we can deeplink %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was under the impression that the tab history JavaScript did this dynamically after the page loads

<%= tab('List', 'fa fa-list', 'home', active: true) %>
<%= tab('Calendar', 'fa fa-calendar', 'calendar', options: { 'data-calendar': calendar_events_path(search_and_facet_params.merge(format: :js, per_page: 200))}) %>
<% if TeSS::Config.map_enabled %>
<%= tab('Map', 'fa fa-globe', 'map',
disabled: { check: (search_and_facet_params[:online] == 'true'),
Expand All @@ -42,6 +44,10 @@
<%= render partial: "search/common/pagination_bar", locals: { resources: @events } %>
</div>

<div id="calendar" class="tab-pane fade in">
<%= render partial: 'events/partials/calendar_loader' %>
</div>

<% if TeSS::Config.map_enabled %>
<div id="map" class="tab-pane fade">
<div id="map-count" class="search-results-count"></div>
Expand Down
19 changes: 19 additions & 0 deletions app/views/events/partials/_calendar.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<%= month_calendar(events: events, attribute: :start, end_attribute: :end) do |date, events| %>
<div class="day-number">
<%= date.day %>
</div>

<ul class="list-unstyled">
<% events.each do |event| %>
<li class= "calendar-text">
<%= link_to "#{event.title}", event, class: 'clear-both', 'title': "#{l event.start, format: :time_short} - #{l event.end, format: :time_short}" %>
</li>
<% end %>
</ul>
<% end %>

<% if events.count < 10 %>
<div >
<%= t('events.pick_date') %>
</div>
<% end %>
8 changes: 8 additions & 0 deletions app/views/events/partials/_calendar_loader.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div id="events_calendar">
<div id='map-loading-screen'>
<%= image_tag('ajax-loader2.gif') %>
</div>
</div>
<div id="events_calendar_long_events">
</div>

13 changes: 13 additions & 0 deletions app/views/events/partials/_calendar_long_events.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<h4><%= t('events.long') %></h4>
<ul>
<% events.each do |event| %>
<li>
<%= link_to event do %>
<%= event.title %>
-

<%= neatly_printed_date_range(event.start, event&.end) %>
<% end %>
</li>
<% end %>
</ul>
38 changes: 0 additions & 38 deletions app/views/events/partials/_events_calendar.html.erb

This file was deleted.

41 changes: 41 additions & 0 deletions app/views/simple_calendar/_month_calendar.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<div id="calendar" class="simple-calendar">
<div class="calendar-heading">
<ul class="pagination">
<li class="prev">
<%= link_to t('simple_calendar.previous', default: 'Previous'), calendar.url_for_previous_view, remote: true %>
</li>
<li>
<span class="calendar-title" style="min-width: 180px; text-align: center;"><%= t('date.month_names')[start_date.month] %> <%= start_date.year %></span>
</li>
<li class="next">
<%= link_to t('simple_calendar.next', default: 'Next'), calendar.url_for_next_view, remote: true %>
</li>
</ul>
</div>

<table class="table table-striped">
<thead>
<tr>
<% date_range.slice(0, 7).each do |day| %>
<th><%= t('date.abbr_day_names')[day.wday] %></th>
<% end %>
</tr>
</thead>

<tbody>
<% date_range.each_slice(7) do |week| %>
<tr>
<% week.each do |day| %>
<%= content_tag :td, class: calendar.td_classes_for(day) do %>
<% if defined?(Haml) && respond_to?(:block_is_haml?) && block_is_haml?(passed_block) %>
<% capture_haml(day, sorted_events.fetch(day, []), &passed_block) %>
<% else %>
<% passed_block.call day, sorted_events.fetch(day, []) %>
<% end %>
<% end %>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
Loading