Commit 4488f502 authored by Zac Wood's avatar Zac Wood

/schedules page, instructors in search

parent 41b03a55
...@@ -105,7 +105,17 @@ nav { ...@@ -105,7 +105,17 @@ nav {
width: 90%; width: 90%;
} }
} }
.instructor {
box-shadow: 0 3px 5px -2px $gray;
border-radius: 5px;
padding: 1em 0 0.5em 1em;
margin-bottom: 1em;
border-top: 5px solid $blue;
a {
text-decoration: underline;
}
}
.course { .course {
box-shadow: 0 3px 5px -2px $gray; box-shadow: 0 3px 5px -2px $gray;
border-radius: 5px; border-radius: 5px;
...@@ -133,6 +143,10 @@ nav { ...@@ -133,6 +143,10 @@ nav {
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
color: $gray; color: $gray;
font-size: 0.9em; font-size: 0.9em;
max-height: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.see-more { .see-more {
...@@ -199,8 +213,9 @@ a { ...@@ -199,8 +213,9 @@ a {
} }
.underline { .underline {
text-decoration: underline; text-decoration: underline !important;
} }
.cart { .cart {
position: fixed; position: fixed;
right: 5%; right: 5%;
...@@ -241,6 +256,7 @@ footer { ...@@ -241,6 +256,7 @@ footer {
color: white; color: white;
background-color: $blue; background-color: $blue;
width: 1.5em; width: 1.5em;
height: 1.5em;
text-align: center; text-align: center;
border-radius: 33%; border-radius: 100%;
} }
# Configures the application. # Configures the application.
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
include BySemester include BySemester
before_action :set_render_page
def set_render_page
@render_page = true
end
end end
class CourseSectionsController < ApplicationController class CourseSectionsController < ApplicationController
def index
@render_page = false
crns = params[:crns].split(',')
@sections = crns.map { |crn| CourseSection.latest_by_crn(crn) }
@days = {
"M" => [], "T" => [], "W" => [],
"R" => [], "F" => []
}
@sections.each do |s|
s.days.split('').each do |day|
@days[day] << s unless s.start_time == "TBA"
end
end
@days_map = {
"M" => "Monday", "T" => "Tuesday", "W" => "Wednesday",
"R" => "Thursday", "F" => "Friday"
}
@days.each do |day, sections|
sections.sort! do |a,b|
Time.new(a.start_time) <=> Time.new(b.start_time)
end
end
end
def show def show
@section = CourseSection.find_by_id(params[:id]) @section = CourseSection.find_by_id(params[:id])
end end
......
...@@ -8,8 +8,6 @@ class CoursesController < ApplicationController ...@@ -8,8 +8,6 @@ class CoursesController < ApplicationController
.joins(:semester) .joins(:semester)
.select("semesters.id") .select("semesters.id")
puts semester_ids.map(&:id)
@semesters = Semester.where(id: semester_ids.map(&:id)) @semesters = Semester.where(id: semester_ids.map(&:id))
@semesters = Semester.sorted_by_date(@semesters) @semesters = Semester.sorted_by_date(@semesters)
......
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files // This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in // present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference // a relevant structure within app/javascript and only use these pack files to reference
...@@ -8,15 +7,19 @@ ...@@ -8,15 +7,19 @@
// layout file, like app/views/layouts/application.html.erb // layout file, like app/views/layouts/application.html.erb
import 'url-polyfill' import 'url-polyfill'
import Turbolinks from 'turbolinks' import Turbolinks from 'turbolinks'
Turbolinks.start()
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('turbolinks:load', () => {
Turbolinks.start() setInitialLinks()
setLinks()
addListeners() addListeners()
document.querySelector('#count').innerText = getCart().length
}) })
function setLinks(crns) {} function setInitialLinks() {
getCart().forEach(writeLink)
}
function addListeners() { function addListeners() {
const links = Array.from(document.querySelectorAll('.add-section')) const links = Array.from(document.querySelectorAll('.add-section'))
...@@ -25,8 +28,7 @@ function addListeners() { ...@@ -25,8 +28,7 @@ function addListeners() {
e.preventDefault() e.preventDefault()
const crn = link.dataset.crn const crn = link.dataset.crn
toggleSection(crn) toggleSection(crn)
console.log(getCart()) writeLink(crn)
writeLinks()
}) })
} }
} }
...@@ -41,6 +43,8 @@ function toggleSection(crn) { ...@@ -41,6 +43,8 @@ function toggleSection(crn) {
} else { } else {
addSection(crn) addSection(crn)
} }
console.log(getCart())
document.querySelector('#count').innerText = getCart().length
} }
function addSection(crn) { function addSection(crn) {
...@@ -53,28 +57,21 @@ function removeSection(crn) { ...@@ -53,28 +57,21 @@ function removeSection(crn) {
localStorage.setItem('cart', JSON.stringify(newCart)) localStorage.setItem('cart', JSON.stringify(newCart))
} }
function writeLinks() {} function writeLink(crn) {
const item = document.querySelector(`[data-crn="${crn}"]`)
if (!item) return
const icon = item.querySelector('.add-remove-link a i')
const link = item.querySelector('.add-remove-link a span')
if (getCart().includes(crn)) {
link.innerText = ' Remove from cart'
icon.className = 'fas fa-minus'
} else {
link.innerText = ' Add Section to Cart'
icon.className = 'fas fa-plus'
}
}
const elementFromString = string => { const elementFromString = string => {
const html = new DOMParser().parseFromString(string, 'text/html') const html = new DOMParser().parseFromString(string, 'text/html')
return html.body.firstChild return html.body.firstChild
} }
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function(callback, type, quality) {
var canvas = this
setTimeout(function() {
var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len)
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i)
}
callback(new Blob([arr], { type: type || 'image/png' }))
})
},
})
}
const initPage = () => {
if (getCart().length != 0) {
document.getElementById('root').innerHTML = '<i class="fas fa-spinner fa-spin"></i>'
fetch(`/course_sections?crns=${getCart().join(',')}`)
.then(resp => resp.text())
.then(text => {
const tree = elementFromString(text)
const body = tree.querySelector('.page')
document.getElementById('root').innerHTML = body.innerHTML
setInitialLinks()
addListeners()
})
} else {
document.getElementById('root').innerHTML = 'Add classes to your cart to see them here!'
}
document.querySelector('#count').innerText = getCart().length
}
window.addEventListener('DOMContentLoaded', initPage)
function setInitialLinks() {
getCart().forEach(writeLink)
}
function addListeners() {
const links = Array.from(document.querySelectorAll('.add-section'))
for (const link of links) {
link.addEventListener('click', e => {
e.preventDefault()
const crn = link.dataset.crn
toggleSection(crn)
writeLink(crn)
})
}
}
function getCart() {
return JSON.parse(localStorage.getItem('cart') || '[]')
}
function toggleSection(crn) {
if (getCart().includes(crn)) {
removeSection(crn)
} else {
addSection(crn)
}
console.log(getCart())
document.querySelector('#count').innerText = getCart().length
}
function addSection(crn) {
const newCart = [...getCart(), crn]
localStorage.setItem('cart', JSON.stringify(newCart))
}
function removeSection(crn) {
const newCart = getCart().filter(c => c !== crn)
localStorage.setItem('cart', JSON.stringify(newCart))
}
function writeLink(crn) {
const item = document.querySelectorAll(`[data-crn="${crn}"]`)
if (item.length == 0) return
item.forEach(item => {
const icon = item.querySelector('.add-remove-link a i')
const link = item.querySelector('.add-remove-link a span')
if (getCart().includes(crn)) {
link.innerText = ' Remove from cart'
icon.className = 'fas fa-minus'
} else {
link.innerText = ' Add Section to Cart'
icon.className = 'fas fa-plus'
}
})
}
const elementFromString = string => {
const html = new DOMParser().parseFromString(string, 'text/html')
return html.body
}
...@@ -43,7 +43,7 @@ class CourseSection < ApplicationRecord ...@@ -43,7 +43,7 @@ class CourseSection < ApplicationRecord
def self.latest_by_crn(crn) def self.latest_by_crn(crn)
sems = Semester.sorted_by_date sems = Semester.sorted_by_date
where(crn: crn).min_by { |s| sems.find_index(s) } where(crn: crn).min_by { |s| sems.find_index(s.semester) }
end end
# Select all course sections that have an instructor that matches the given name # Select all course sections that have an instructor that matches the given name
......
<% @days.each do |day, sections| %>
<% next if sections.empty? %>
<h4 style="margin-top: 0.5em"><%= @days_map[day] %></h4>
<ul class="section-list">
<%= render(partial: 'shared/section', collection: sections) %>
</ul>
<% end %>
\ No newline at end of file
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
<div class="row"> <div class="row">
<div class="col-lg-4 col-12 mb-4"> <div class="col-lg-4 col-12 mb-4">
<h1><%= @instructor.name %></h1> <h1><%= @instructor.name %></h1>
<% unless @rating[:teaching].nil? %> <% unless @rating[:teaching].nil? %>
<%= render(partial: 'shared/stars', locals: { percent: @rating[:teaching][0]/5*100 }) %> <%= render(partial: 'shared/stars', locals: { percent: @rating[:teaching][0]/5*100 }) %>
<%= @rating[:teaching][0] %> / <%= @rating[:teaching][1] %> responses <%= @rating[:teaching][0] %> / <%= @rating[:teaching][1] %> responses
<% end %> <% end %>
</div> </div>
<div class="col-lg-8 col-12"> <div class="col-lg-8 col-12">
......
...@@ -44,8 +44,14 @@ ...@@ -44,8 +44,14 @@
</head> </head>
<body> <body>
<%= render 'shared/page' do %> <% if @render_page %>
<%= render 'shared/page' do %>
<%= yield %>
<% end %>
<% else %>
<div class="page">
<%= yield %> <%= yield %>
</div>
<% end %> <% end %>
<!-- Matomo --> <!-- Matomo -->
......
<%= javascript_pack_tag 'schedules' %> <%= render(partial: 'shared/navbar') %>
<%= stylesheet_link_tag 'schedules' %>
<h2 style="margin-bottom: 0.5em">Your Schedule</h2>
<div id="root"></div> <div id="root"></div>
<%= javascript_pack_tag('schedules') %>
\ No newline at end of file
<%= render(partial: 'shared/navbar') %> <%= render(partial: 'shared/navbar') %>
<div class="search-results"> <div class="search-results">
<% @courses.each do |course| %> <% (@instructors || []).each do |inst| %>
<section class="instructor">
<h5><a href="/instructors/<%= inst.id %>"> <%= inst.name %></a></h5>
</section>
<% end %>
<% (@courses || []).each do |course| %>
<section class="course"> <section class="course">
<h4><a href="/courses/<%= course['id'] %>"><%= course['subject'] %> <%= course['course_number'] %></a></h4> <h4><a href="/courses/<%= course['id'] %>"><%= course['subject'] %> <%= course['course_number'] %></a></h4>
<h5><em><%= course['title'] %>. <%= course['credits'] %> <%= 'credit'.pluralize(course['credits'].to_i) %>.</em></h5> <h5><em><%= course['title'] %>. <%= course['credits'] %> <%= 'credit'.pluralize(course['credits'].to_i) %>.</em></h5>
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
</div> </div>
<%# </div> %> <%# </div> %>
<div class="cart"> <a class="cart" href="/schedule" data-turbolinks="false">
<span id="count">1</span> <span id="count">0</span>
<i class="fas fa-shopping-cart"></i> <i class="fas fa-shopping-cart"></i>
</div> </a>
<footer class="footer"> <footer class="footer">
Schedules was built by <a href="https://srct.gmu.edu">Mason SRCT</a> and is completely open source. <br/> Schedules was built by <a href="https://srct.gmu.edu">Mason SRCT</a> and is completely open source. <br/>
......
...@@ -26,5 +26,10 @@ ...@@ -26,5 +26,10 @@
</div> </div>
<% end %> <% end %>
<div class="add-remove-link"><i class="fas fa-plus"></i><a data-crn="<%= "#{section.crn}" %>" class="underline add-section">Add Section to Cart</a></div> <div class="add-remove-link">
<a data-crn="<%= "#{section.crn}" %>" class="underline add-section">
<i class="fas fa-plus add-remove-icon"></i>
<span>Add Section to Cart</span>
</a>
</div>
</li> </li>
...@@ -9,7 +9,7 @@ Rails.application.routes.draw do ...@@ -9,7 +9,7 @@ Rails.application.routes.draw do
get 'sessions/add_bulk' get 'sessions/add_bulk'
resources :courses, only: [:show] resources :courses, only: [:show]
resources :course_sections, only: [:show] resources :course_sections, only: [:index, :show]
resources :instructors, only: [:index, :show] resources :instructors, only: [:index, :show]
get 'schedule', to: 'schedules#show', as: 'schedule' get 'schedule', to: 'schedules#show', as: 'schedule'
get 'schedule/events', to: 'schedules#events' get 'schedule/events', to: 'schedules#events'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment