Commit 9d104142 authored by Zac Wood's avatar Zac Wood

fixed course card header

parent 6c8d6b74
...@@ -24,7 +24,7 @@ const elementFromString = string => { ...@@ -24,7 +24,7 @@ const elementFromString = string => {
}; };
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
this.schedule = new Schedule(); this.cart = new Cart();
}); });
const setSemester = async select => { const setSemester = async select => {
......
class Schedule { class Cart {
constructor() { constructor() {
this.isOpen = false; this.isOpen = false;
this._courses = {}; // {title, id, sections: {id, crn}} this._courses = {}; // {title, id, sections: {id, crn}}
...@@ -12,7 +12,7 @@ class Schedule { ...@@ -12,7 +12,7 @@ class Schedule {
} }
document.getElementById('course-counter').innerText = Object.keys(this._courses).length; document.getElementById('course-counter').innerText = Object.keys(this._courses).length;
this._ids = Array.from(document.getElementById('schedule').children).map(e => e.dataset.crn); this._ids = Array.from(document.getElementById('cart-courses').children).map(e => e.dataset.crn);
} }
get crns() { get crns() {
...@@ -45,12 +45,12 @@ class Schedule { ...@@ -45,12 +45,12 @@ class Schedule {
addCourse(course) { addCourse(course) {
this._courses[course.id] = course; this._courses[course.id] = course;
const parent = document.querySelector('#schedule'); const courseList = document.getElementById('cart-courses');
const current = parent.querySelector(`#schedule-${course.id}`); const courseNode = courseList.querySelector(`#schedule-${course.id}`);
const newNode = this._constructCourseNode(course); const newNode = this._constructCourseNode(course);
if (current !== null) parent.replaceChild(newNode, current); if (courseNode !== null) courseList.replaceChild(newNode, courseNode);
else parent.appendChild(newNode); else courseList.appendChild(newNode);
document.getElementById('course-counter').innerText = Object.keys(this._courses).length; document.getElementById('course-counter').innerText = Object.keys(this._courses).length;
fetch(`/sessions/update?section_ids=${this.ids.join(',')}`, { cache: 'no-store' }); fetch(`/sessions/update?section_ids=${this.ids.join(',')}`, { cache: 'no-store' });
...@@ -65,9 +65,9 @@ class Schedule { ...@@ -65,9 +65,9 @@ class Schedule {
delete this._courses[id]; delete this._courses[id];
const parent = document.querySelector('#schedule'); const courseList = document.getElementById('cart-courses');
const current = parent.querySelector(`#schedule-${id}`); const current = courseList.querySelector(`#schedule-${id}`);
parent.removeChild(current); courseList.removeChild(current);
document.getElementById('course-counter').innerText = Object.keys(this._courses).length; document.getElementById('course-counter').innerText = Object.keys(this._courses).length;
fetch(`/sessions/update?section_ids=${this.ids.join(',')}`, { cache: 'no-store' }); fetch(`/sessions/update?section_ids=${this.ids.join(',')}`, { cache: 'no-store' });
...@@ -93,7 +93,7 @@ class Schedule { ...@@ -93,7 +93,7 @@ class Schedule {
if (course) { if (course) {
course.sections.push(section); course.sections.push(section);
const courseNode = document.querySelector('#schedule').querySelector(`#schedule-${course.id}`); const courseNode = document.getElementById(`#schedule-${course.id}`);
const crnList = courseNode.querySelector('.crns'); const crnList = courseNode.querySelector('.crns');
crnList.innerText = course.sections.map(s => `#${s.crn}`); crnList.innerText = course.sections.map(s => `#${s.crn}`);
...@@ -109,7 +109,7 @@ class Schedule { ...@@ -109,7 +109,7 @@ class Schedule {
removeSection(section) { removeSection(section) {
const course = this.courseContainingSection(section.id); const course = this.courseContainingSection(section.id);
course.sections = course.sections.filter(s => s.id !== section.id); course.sections = course.sections.filter(s => s.id !== section.id);
const schedule = document.querySelector('#schedule'); const schedule = document.querySelector('#cart-courses');
const courseNode = schedule.querySelector(`#schedule-${course.id}`); const courseNode = schedule.querySelector(`#schedule-${course.id}`);
const crnList = courseNode.querySelector('.crns'); const crnList = courseNode.querySelector('.crns');
if (course.sections.length === 0) { if (course.sections.length === 0) {
...@@ -148,5 +148,5 @@ class Schedule { ...@@ -148,5 +148,5 @@ class Schedule {
} }
const removeCourse = id => { const removeCourse = id => {
this.schedule.removeCourse(id); this.cart.removeCourse(id);
}; };
document.addEventListener('DOMContentLoaded', () => {
const eventsTemplate = document.querySelector('#events');
if (eventsTemplate) {
const eventsJSON = eventsTemplate.dataset.events;
const events = JSON.parse(eventsJSON);
console.log(events);
$('#calendar').fullCalendar({
defaultDate: new Date(2019, 0, 14),
defaultView: 'agendaWeek',
header: false,
events: events,
});
}
});
...@@ -9,23 +9,23 @@ const addCourse = (event, id) => { ...@@ -9,23 +9,23 @@ const addCourse = (event, id) => {
const sectionsItems = Array.from(courseCard.querySelectorAll('li')); const sectionsItems = Array.from(courseCard.querySelectorAll('li'));
const sections = sectionsItems.map(li => ({ ...li.dataset })); const sections = sectionsItems.map(li => ({ ...li.dataset }));
this.schedule.addCourse({ title, id, sections }); this.cart.addCourse({ title, id, sections });
sectionsItems.forEach(s => s.classList.add('selected')); sectionsItems.forEach(s => s.classList.add('selected'));
event.stopPropagation(); event.stopPropagation();
}; };
/** /**
* Either adds or removes a section from the schedule depending on * Either adds or removes a section from the cart depending on
* if it is currently in the schedule. * if it is currently in the cart.
*/ */
const addOrRemoveFromSchedule = (event, sectionNode) => { const addOrRemoveFromCart = (event, sectionNode) => {
const section = { ...sectionNode.dataset }; const section = { ...sectionNode.dataset };
if (this.schedule.includesSection(section.id)) { if (this.cart.includesSection(section.id)) {
this.schedule.removeSection(section); this.cart.removeSection(section);
sectionNode.classList.remove('selected'); sectionNode.classList.remove('selected');
} else { } else {
this.schedule.addSection(section); this.cart.addSection(section);
sectionNode.classList.add('selected'); sectionNode.classList.add('selected');
} }
...@@ -33,15 +33,15 @@ const addOrRemoveFromSchedule = (event, sectionNode) => { ...@@ -33,15 +33,15 @@ const addOrRemoveFromSchedule = (event, sectionNode) => {
}; };
/** /**
* Removes a given section from the schedule * Removes a given section from the cart
* @param {Node} DOM Node of the Section in the schedule * @param {Node} DOM Node of the Section in the cart
*/ */
const removeFromSchedule = section => { const removeFromCart = section => {
const sectionInSearch = sectionWithCrn(section.dataset.crn); const sectionInSearch = sectionWithCrn(section.dataset.crn);
if (sectionInSearch) { if (sectionInSearch) {
sectionInSearch.classList.remove('selected'); sectionInSearch.classList.remove('selected');
} }
this.schedule.removeFromSchedule(section.dataset.crn); this.cart.removeFromSchedule(section.dataset.crn);
}; };
/** /**
...@@ -62,5 +62,5 @@ const toggleSections = course => { ...@@ -62,5 +62,5 @@ const toggleSections = course => {
* and sets the link in the modal to it. * and sets the link in the modal to it.
*/ */
const setUrlInModal = () => { const setUrlInModal = () => {
document.getElementById('calendar-link').innerText = `https://${window.location.hostname}/api/schedule?crns=${this.schedule.ids.join(',')}`; document.getElementById('calendar-link').innerText = `https://${window.location.hostname}/api/schedule?crns=${this.cart.ids.join(',')}`;
}; };
...@@ -27,23 +27,26 @@ body { ...@@ -27,23 +27,26 @@ body {
box-shadow: 0 0 5px rgba(0,0,0,0.2); box-shadow: 0 0 5px rgba(0,0,0,0.2);
transition: 0.3s; transition: 0.3s;
.card-header { .card-header {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.card-body { .card-body {
.attr-list { .attr-list {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start;
padding-bottom: 10px; .attr {
.icon {
.attr { padding-right: 4px;
margin-right: 13px; }
} align-items: center;
} display: inline-flex;
white-space: nowrap;
}
}
} }
} }
...@@ -98,35 +101,8 @@ body { ...@@ -98,35 +101,8 @@ body {
align-items: center; align-items: center;
} }
#navbar {
margin-top: 8px;
margin-bottom: 48px;
}
#logo {
font-size: 24pt;
color: black;
}
.form-control:focus { .form-control:focus {
border-color: transparent; border-color: transparent;
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(0, 0, 255, 0.5); box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(0, 0, 255, 0.5);
} }
#cart {
display: none;
}
.card .small {
margin-bottom: 16px;
padding: 12px;
}
#add-course-btn:hover {
background-color: rgba(0,0,0,0.2);
}
#calendar {
background-color: white;
padding: 16px;
}
.cart-course {
display: flex; justify-content: space-between;
.title {
min-width: 15%;
}
.crns {
color: gray;
font-size: 10pt;
}
}
#cart {
display: none;
}
// Place all the styles related to the Courses controller here. // Place all the styles related to the Courses controller here.
// They will automatically be included in application.css. // They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/ // You can use Sass (SCSS) here: http://sass-lang.com/
#add-course-btn:hover {
background-color: rgba(0,0,0,0.2);
}
.center-vert {
display: flex;
justify-content: center;
align-items: center;
}
#navbar {
margin-top: 8px;
margin-bottom: 48px;
}
#logo {
font-size: 24pt;
color: black;
}
#calendar {
background-color: white;
padding: 16px;
}
// Place all the styles related to the sessions controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
...@@ -25,11 +25,15 @@ class SchedulesController < ApplicationController ...@@ -25,11 +25,15 @@ class SchedulesController < ApplicationController
"S": Date.new(2019, 1, 19), "S": Date.new(2019, 1, 19),
"U": Date.new(2019, 1, 20) "U": Date.new(2019, 1, 20)
}.freeze }.freeze
def show def show
all_sections = @cart.values
# schedules = []
all_sections.each_with_index do |sections, i|
end
@events = @cart.map do |_cid, sections| @events = @cart.map do |_cid, sections|
s = sections.first s = sections.first
s.days.split('').map do |day| s.days.split('').map do |day|
formatted_date = DAYS[day.to_sym].to_s.tr('-', '') formatted_date = DAYS[day.to_sym].to_s.tr('-', '')
time = Time.parse(s.start_time).strftime("%H%M%S") time = Time.parse(s.start_time).strftime("%H%M%S")
...@@ -41,7 +45,6 @@ class SchedulesController < ApplicationController ...@@ -41,7 +45,6 @@ class SchedulesController < ApplicationController
end: "#{formatted_date}T#{endtime}" end: "#{formatted_date}T#{endtime}"
} }
end end
end.flatten end.flatten
end end
end end
class SearchController < ApplicationController class SearchController < ApplicationController
def index def index
@results = SearchHelper::GenericItem.fetchall(params[:query], semester: @semester) results = SearchHelper::GenericItem.fetchall(params[:query], semester: @semester).group_by(&:type)
@instructors = results[:instructor]
@courses = results[:course]
end end
end end
...@@ -29,9 +29,10 @@ module SearchHelper ...@@ -29,9 +29,10 @@ module SearchHelper
end end
def self.fetch_instructors(query_data) def self.fetch_instructors(query_data)
Instructor.from_name(Instructor.select("instructors.*, COUNT(course_sections.id) AS section_count"), query_data.search_string) Instructor
.left_outer_joins(:course_sections) .from_name(Instructor.select("instructors.*, COUNT(course_sections.id) AS section_count"), query_data.search_string)
.group("instructors.id") .left_outer_joins(:course_sections)
.group("instructors.id")
end end
def self.fetch_courses(query_data) def self.fetch_courses(query_data)
......
...@@ -13,9 +13,9 @@ class CourseSection < ApplicationRecord ...@@ -13,9 +13,9 @@ class CourseSection < ApplicationRecord
def labs def labs
return nil unless section_type == "Lecture" return nil unless section_type == "Lecture"
lecture_number = name.split[name.split.length-1] lecture_number = name.split[name.split.length - 1]
course.course_sections.select do |s| course.course_sections.select do |s|
s.title.split[s.title.split.length-1] == lecture_number s.title.split[s.title.split.length - 1] == lecture_number
end end
end end
......
...@@ -7,16 +7,6 @@ ...@@ -7,16 +7,6 @@
* var calendar = new Calendar(cal, { * var calendar = new Calendar(cal, {
* defaultView: 'agendaWeek' * defaultView: 'agendaWeek'
* }); */ * }); */
document.addEventListener('DOMContentLoaded', () => {
const eventsJSON = document.querySelector('#events').dataset.events;
const events = JSON.parse(eventsJSON);
console.log(events);
$('#calendar').fullCalendar({
defaultDate: new Date(2019, 0, 14),
defaultView: 'agendaWeek',
header: { right: 'next'},
events: events
});
});
</script> </script>
<% if @results.any? %> <% unless @instructors.nil? %>
<% @results.each do |result| %> <h2>Instructors</h2>
<% if result.type == :course %> <div class="row">
<%= render partial: 'shared/course', object: result.data %> <% @instructors.map(&:data).each do |instructor| %>
<% elsif result.type == :instructor %> <div class="col"><%= render partial: 'shared/instructor', object: instructor %></div>
<%= render partial: 'shared/instructor', object: result.data %> <% end %>
</div>
<hr />
<% end %>
<% unless @courses.nil? %>
<h2>Courses</h2>
<% @courses.map(&:data).each do |course| %>
<div class="col"><%= render partial: 'shared/course', object: course %></div>
<% end %> <% end %>
<% end %>
<% else %>
<h1>Sorry, we couldn't find anything matching your search.</h1>
<p>Please try again!</p>
<% end %> <% end %>
<% if @courses.nil? && @instructors.nil? %>
<h1>Sorry, we couldn't find anything matching your search.</h1>
<p>Please try again!</p>
<% end %>
...@@ -4,26 +4,21 @@ ...@@ -4,26 +4,21 @@
<div class="col order-1 order-lg-1" id="cart"> <div class="col order-1 order-lg-1" id="cart">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h3 class="card-title"><%= link_to 'Your Schedule', schedule_path %></h3> <h3 class="card-title"><%= link_to 'Your Cart', schedule_path %></h3>
</div> </div>
<ul class="list-group list-group-flush" id="schedule"> <ul class="list-group list-group-flush" id="cart-courses">
<% @cart.each do |cid, sections| %> <% @cart.each do |cid, sections| %>
<% course = Course.find_by_id(cid) %> <% course = Course.find_by_id(cid) %>
<li id="schedule-<%= cid %>" class="list-group-item schedule-section-card" onclick="removeCourse(<%= cid %>)"> <li id="schedule-<%= cid %>" class="list-group-item" onclick="removeCourse(<%= cid %>)">
<div style="display: flex; justify-content: space-between;"> <div class="cart-course">
<b style="min-width: 15%"><%= "#{course.subject} #{course.course_number}" %></b> <b class="title"><%= "#{course.subject} #{course.course_number}" %></b>
<span class="crns" style="color: gray; font-size: 10pt;"> <span class="crns">
<%= sections.map { |s| "##{s.crn}" }.join(', ') %> <%= sections.map { |s| "##{s.crn}" }.join(', ') %>
</span> </span>
</div> </div>
</li> </li>
<% end %> <% end %>
</ul> </ul>
<div class="card-body">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exportModal" onclick="setUrlInModal()">
Export schedule
</button>
</div>
</div> </div>
</div> </div>
......
...@@ -2,16 +2,34 @@ ...@@ -2,16 +2,34 @@
<div class="card" id="course-<%= course.id %>" onclick="toggleSections(this)"> <div class="card" id="course-<%= course.id %>" onclick="toggleSections(this)">
<div class="card-header"> <div class="card-header">
<h4 class="title"><%= "#{course.subject} #{course.course_number}" %></h4> <div class="row">
<h5><em><%= course.title %></em>. <%= course.credits %> credits.</h5> <div class="col">
<h4 id="add-course-btn" onclick="addCourse(event, '<%= course.id %>');"> <h4 class="title"><%= "#{course.subject} #{course.course_number}" %></h4>
<i class="fas fa-plus" style="color: green"></i> </div>
</h4>
<div class="col">
<h4 id="add-course-btn" class="text-right" onclick="addCourse(event, '<%= course.id %>');">
<i class="fas fa-plus" style="color: green"></i>
</h4>
</div>
</div>
<h5><em><%= course.title %></em></h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="attr-list"> <div class="attr-list justify-content-start">
<div class="attr"><i class="fa fa-book"></i> 3 credits</div> <div class="attr">
<div class="attr"><i class="fa fa-bars"></i> 3 sections</div> <div class="icon">
<i class="fa fa-book"></i>
</div>
3 credits
</div>
&nbsp;&nbsp;&nbsp;
<div class="attr">
<div class="icon">
<i class="fa fa-bars"></i>
</div>
3 sections
</div>
</div> </div>
<p class="description"><%= course.description %></p> <p class="description"><%= course.description %></p>
......
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-body">
<h4><%= "#{instructor.name}" %></h4> <div class="attr-list justify-content-between">
</div> <div class="attr">
<div class="card-body"> <div class="icon">
<div class="attr-list"> <i class="fa fa-user"></i>
<div class="attr"><i class="fa fa-user"></i> Instructor</div> </div>
<div class="attr"><i class="fa fa-bars"></i> <%= "#{instructor.section_count}" %> sections</div> <%= link_to instructor.name, instructor_path(instructor) %>
</div>
&nbsp;&nbsp;&nbsp;
<div class="attr">
<div class="icon">
<i class="fa fa-bars"></i>
</div>
<%= "#{instructor.section_count}" %> sections
</div>
</div>