diff --git a/app/assets/javascripts/osem-schedule.js b/app/assets/javascripts/osem-schedule.js index baae1933..683735af 100644 --- a/app/assets/javascripts/osem-schedule.js +++ b/app/assets/javascripts/osem-schedule.js @@ -1,3 +1,5 @@ +// ADMIN SCHEDULE + var url; // Should be initialize in Schedule.initialize var schedule_id; // Should be initialize in Schedule.initialize @@ -117,6 +119,31 @@ $(document).ready( function() { }); }); + +// PUBLIC SCHEDULE + +function starClicked(e){ + // stops the click from propagating + if (!e) var e = window.event; + e.preventDefault(); + e.cancelBubble = true; + if (e.stopPropagation) e.stopPropagation(); + + var callback = function(data) { + $(e.target).toggleClass('fa-star fa-star-o'); + } + + var params = { favourite_user_id: $(e.target).data('user') }; + + $.ajax({ + url: $(e.target).data('url'), + type: 'PATCH', + data: params, + success: callback, + dataType : 'json' + }); +} + function eventClicked(e, element){ var url = $(element).data('url'); if(e.ctrlKey) diff --git a/app/assets/stylesheets/osem-schedule.css.scss b/app/assets/stylesheets/osem-schedule.css.scss index f2a97b83..3cab9cb7 100644 --- a/app/assets/stylesheets/osem-schedule.css.scss +++ b/app/assets/stylesheets/osem-schedule.css.scss @@ -127,7 +127,7 @@ a.unstyled-link { .program-dropdown, .schedule-dropdown{ margin-left: 20%; margin-right: 20%; - margin-top: 40px; + margin-top: 10px; margin-bottom: 20px; } @@ -234,20 +234,6 @@ td.no-padding{ cursor: pointer; } -.program-dropdown{ - margin-left: 20%; - margin-right: 20%; - margin-top: 40px; -} - -.program-dropdown > button{ - width: 100%; -} - -.program-dropdown > .dropdown-menu{ - width: 100%; -} - .no-events-day{ color: #D8D8D8 !important; } @@ -263,6 +249,11 @@ td.no-padding{ font-size: 7px; } +#star-events{ + margin-right: 5px; + vertical-align: text-top; +} + /* Small devices (tablets, 768px and up) */ @media (min-width: 768px) { .room, .event-title{ diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb index 3ef8bd5f..9ed32c2f 100644 --- a/app/controllers/proposals_controller.rb +++ b/app/controllers/proposals_controller.rb @@ -1,10 +1,11 @@ class ProposalsController < ApplicationController + skip_authorization_check before_action :authenticate_user!, except: [:show, :new, :create] load_resource :conference, find_by: :short_title load_resource :program, through: :conference, singleton: true load_and_authorize_resource :event, parent: false, through: :program # We authorize manually in these actions - skip_authorize_resource :event, only: [:confirm, :restart, :withdraw] + skip_authorize_resource :event, only: [:confirm, :restart, :toogle_favorite, :withdraw] def index @event = @program.events.new @@ -74,6 +75,17 @@ class ProposalsController < ApplicationController end end + def toogle_favorite + user = User.find(params[:favourite_user_id]) + users = @event.favourite_users + if users.include? user + @event.favourite_users.delete(user) + else + @event.favourite_users << User.find(params[:favourite_user_id]) + end + render json: {} + end + def withdraw authorize! :update, @event @url = conference_program_proposal_path(@conference.short_title, params[:id]) diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index 0b135328..8d6e37a1 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -1,6 +1,7 @@ class SchedulesController < ApplicationController protect_from_forgery with: :null_session before_action :respond_to_options + before_action :favourites load_and_authorize_resource :conference, find_by: :short_title load_resource :program, through: :conference, singleton: true, except: :index @@ -29,13 +30,16 @@ class SchedulesController < ApplicationController @dates = @conference.start_date..@conference.end_date @events_schedules = @program.selected_event_schedules + @events_schedules = @events_schedules.select{ |e| e.event.favourite_users.exists?(current_user.id) } if @events_schedules && current_user && @favourites @events_schedules = [] unless @events_schedules + @favourites = params[:favourites] == 'true' @unscheduled_events = if @program.selected_schedule @program.events.confirmed - @program.selected_schedule.events else @program.events.confirmed end + @unscheduled_events = @unscheduled_events.select{ |e| e.favourite_users.exists?(current_user.id) } if current_user && @favourites day = @conference.current_conference_day @tag = day.strftime('%Y-%m-%d') if day @@ -43,6 +47,10 @@ class SchedulesController < ApplicationController private + def favourites + @favourites = params[:favourites] == 'true' + end + def respond_to_options respond_to do |format| format.html { head :ok } diff --git a/app/models/event.rb b/app/models/event.rb index 1eebb0b0..63125f20 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -22,8 +22,11 @@ class Event < ActiveRecord::Base belongs_to :difficulty_level belongs_to :program + has_and_belongs_to_many :favourite_users, class_name: 'User' + accepts_nested_attributes_for :event_users, allow_destroy: true accepts_nested_attributes_for :users + accepts_nested_attributes_for :favourite_users before_create :generate_guid diff --git a/app/models/user.rb b/app/models/user.rb index 05edcce6..580e11e1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -49,6 +49,9 @@ class User < ActiveRecord::Base has_many :votes, dependent: :destroy has_many :voted_events, through: :votes, source: :events has_many :subscriptions, dependent: :destroy + + has_and_belongs_to_many :favourite_events, class_name: 'Event' + accepts_nested_attributes_for :roles scope :admin, -> { where(is_admin: true) } diff --git a/app/views/schedules/_carousel.html.haml b/app/views/schedules/_carousel.html.haml index 5c5acc29..0bbab166 100644 --- a/app/views/schedules/_carousel.html.haml +++ b/app/views/schedules/_carousel.html.haml @@ -30,6 +30,7 @@ .room.elipsis.break-words{ style: "-webkit-line-clamp: #{ room_lines(@rooms) }; height: #{ room_height(@rooms) }px;" } = room.name - event_schedules = room.event_schedules.select{ |e| (e.schedule_id == @conference.program.selected_schedule.id) && (e.end_time > start_time) && (e.start_time <= (start_time + hrs_per_slide.hour)) } + - event_schedules = event_schedules.select{ |e| e.event.favourite_users.exists?(current_user.id) } if current_user && @favourites - (1..intervals).each do |i| - if span > 1 - span -= 1 diff --git a/app/views/schedules/_event.html.haml b/app/views/schedules/_event.html.haml index 49116e42..7afcac48 100644 --- a/app/views/schedules/_event.html.haml +++ b/app/views/schedules/_event.html.haml @@ -4,11 +4,15 @@ = image_tag speaker.gravatar_url, :class => "img-circle pull-right all-speaker-pic", | :alt => speaker.name, | :title => speaker.name | - %p = canceled_replacement_event_label(event, event_schedule) = replacement_event_notice(event_schedule) - + - if current_user + = link_to('#', onClick: 'starClicked();') do + %span#star-events{ class: "fa fa-lg #{ event.favourite_users.exists?(current_user.id) ? 'fa-star' : 'fa-star-o' }", | + "aria-hidden" => "true", | + "data-url" => toogle_favorite_conference_program_proposal_path(@conference.short_title, event.id), | + "data-user" => current_user.id } %span.h3 = event.title %br diff --git a/app/views/schedules/_schedule_item.html.haml b/app/views/schedules/_schedule_item.html.haml index ffed1716..9d8ab42c 100644 --- a/app/views/schedules/_schedule_item.html.haml +++ b/app/views/schedules/_schedule_item.html.haml @@ -1,12 +1,18 @@ %td.event{ width: "#{ width * span }%" , | colspan: span, | role: "button" } - %a.unstyled-link{href: url_for(conference_program_proposal_path(@conference.short_title, event.id))} + %div{ onClick: 'eventClicked(event, this);', "data-url" => "#{url_for(conference_program_proposal_path(@conference.short_title, event.id))}" } %div{ class: "elipsis break-words event-title", | style: "-webkit-line-clamp: #{ event_lines(@rooms) }; height: #{ event_height(@rooms) }px;"} | = canceled_replacement_event_label(event, event_schedule, 'schedule-label') + - if current_user + = link_to('#', onClick: 'starClicked();') do + %span#star{ class: "fa fa-lg #{ event.favourite_users.exists?(current_user.id) ? 'fa-star' : 'fa-star-o' }", | + "aria-hidden" =>"true", | + "data-url" => toogle_favorite_conference_program_proposal_path(@conference.short_title, event.id), | + "data-user" => current_user.id } = event.title - if speaker = event.speakers.first diff --git a/app/views/schedules/_schedule_tabs.html.haml b/app/views/schedules/_schedule_tabs.html.haml index 0317f8bc..30dd5ab7 100644 --- a/app/views/schedules/_schedule_tabs.html.haml +++ b/app/views/schedules/_schedule_tabs.html.haml @@ -2,6 +2,6 @@ / Nav tabs %ul.nav.nav-tabs{ role: "tablist" } %li{ class: "schedule #{ 'active' if active == 'schedule' }", role: "presentation" } - = link_to('Schedule', conference_schedule_path(@conference.short_title)) + = link_to('Schedule', conference_schedule_path(@conference.short_title, favourites: @favourites)) %li{ class: "program #{ 'active' if active == 'program' }", role: "presentation" } - = link_to('All events', events_conference_schedule_path(@conference.short_title)) + = link_to('All events', events_conference_schedule_path(@conference.short_title, favourites: @favourites)) diff --git a/app/views/schedules/events.html.haml b/app/views/schedules/events.html.haml index 8680d3c3..b63cb184 100644 --- a/app/views/schedules/events.html.haml +++ b/app/views/schedules/events.html.haml @@ -5,6 +5,13 @@ %h1.text-center Program for = @conference.title + - if current_user + .row + .col-md-12.text-center + = button_to (@favourites ? 'All events' : 'Only favourites events'), + events_conference_schedule_path(@conference.short_title), + params: { favourites: !@favourites }, + method: :get, class: 'btn btn-success' .dropdown.program-dropdown %button{ type: "button", class: "btn btn-default dropdown-toggle", 'data-toggle' => "dropdown" } Dates diff --git a/app/views/schedules/show.html.haml b/app/views/schedules/show.html.haml index bd0f9976..d73a9107 100644 --- a/app/views/schedules/show.html.haml +++ b/app/views/schedules/show.html.haml @@ -5,6 +5,13 @@ %h1.text-center Schedule for = @conference.title + - if current_user + .row + .col-md-12.text-center + = button_to (@favourites ? 'All events' : 'Only favourites events'), + conference_schedule_path(@conference.short_title), + params: { favourites: !@favourites }, + method: :get, class: 'btn btn-success' .dropdown.schedule-dropdown %button{ type: "button", class: "btn btn-default dropdown-toggle", 'data-toggle' => "dropdown" } = @day diff --git a/config/routes.rb b/config/routes.rb index ba9228aa..64a3d522 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -115,6 +115,7 @@ Osem::Application.routes.draw do get :registrations patch '/confirm' => 'proposals#confirm' patch '/restart' => 'proposals#restart' + patch :toogle_favorite end end end diff --git a/db/migrate/20160811143427_create_events_users.rb b/db/migrate/20160811143427_create_events_users.rb new file mode 100644 index 00000000..64a7fa6e --- /dev/null +++ b/db/migrate/20160811143427_create_events_users.rb @@ -0,0 +1,8 @@ +class CreateEventsUsers < ActiveRecord::Migration + def change + create_table :events_users, id: false do |t| + t.references :event + t.references :user + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 34c30a04..4ac79b13 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -243,6 +243,11 @@ ActiveRecord::Schema.define(version: 20170213145807) do t.datetime "created_at" end + create_table "events_users", id: false, force: :cascade do |t| + t.integer "event_id" + t.integer "user_id" + end + create_table "lodgings", force: :cascade do |t| t.string "name" t.text "description"