Commit c9161a32 authored by Zac Wood's avatar Zac Wood
Browse files

Merge branch 'dev-v2' into 'master'

Dev v2

See merge request !36
parents 8970cc67 160d7746
Pipeline #3490 passed with stage
in 2 minutes and 22 seconds
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
{
"compilerOptions": {
"lib": ["es2015", "dom"]
}
}
document.addEventListener('DOMContentLoaded', () => {
const eventsTemplate = document.querySelector('#events');
if (eventsTemplate) {
const eventsJSON = eventsTemplate.dataset.events;
const events = JSON.parse(eventsJSON);
window.events = events;
$('#calendar').fullCalendar({
defaultDate: new Date(2019, 0, 14),
defaultView: 'agendaWeek',
header: false,
events: renderEvents,
columnHeaderFormat: 'dddd',
allDaySlot: false,
});
}
initListeners();
});
const renderEvents = (start, end, timezone, callback) => {
callback(window.events);
};
const remove = async item => {
await window.cart.toggleSection({ ...item.dataset });
location.reload(true);
};
/**
* Generates a URL for the current sections in the schedule
* and sets the link in the modal to it.
*/
const setUrlInModal = () => {
document.getElementById('calendar-link').innerText = `${window.location.protocol}//${window.location.hostname}/api/schedules?section_ids=${window.cart._courses.join(',')}`;
};
const downloadIcs = async () => {
const response = await fetch(`${window.location.protocol}//${window.location.hostname}/api/schedules?section_ids=${window.cart._courses.join(',')}`);
const text = await response.text();
const blob = new Blob([text], { type: 'text/calendar;charset=utf-8' });
saveAs(blob, 'GMU Schedule.ics');
};
const addToSystemCalendar = () => {
window.open(`webcal://${window.location.hostname}/api/schedules?section_ids=${window.cart._courses.join(',')}`);
};
const initListeners = () => {
const items = Array.from(document.querySelectorAll('.section-item'));
items.forEach(item => (item.onclick = () => remove(item)));
document.getElementById('open-modal-btn').onclick = setUrlInModal;
document.getElementById('download-ics').onclick = downloadIcs;
document.getElementById('add-to-system').onclick = addToSystemCalendar;
document.getElementById('share-url').innerText = `${window.location.protocol}//${window.location.hostname}/schedule/view?section_ids=${window.cart._courses.join(',')}`;
};
document.addEventListener('DOMContentLoaded', () => {
const eventsTemplate = document.querySelector('#events');
if (eventsTemplate) {
const eventsJSON = eventsTemplate.dataset.events;
const events = JSON.parse(eventsJSON);
window.events = events;
$('#calendar').fullCalendar({
defaultDate: new Date(2019, 0, 14),
defaultView: 'agendaWeek',
header: false,
events: renderEvents,
columnHeaderFormat: 'dddd',
allDaySlot: false,
});
}
initListeners();
});
const renderEvents = (start, end, timezone, callback) => {
callback(window.events);
};
/**
* Generates a URL for the current sections in the schedule
* and sets the link in the modal to it.
*/
const setUrlInModal = () => {
document.getElementById('calendar-link').innerText = `${window.location.protocol}//${window.location.hostname}/api/schedules?section_ids=${window.cart._courses.join(',')}`;
};
const downloadIcs = async () => {
const response = await fetch(`${window.location.protocol}//${window.location.hostname}/api/schedules?section_ids=${window.cart._courses.join(',')}`);
const text = await response.text();
const blob = new Blob([text], { type: 'text/calendar;charset=utf-8' });
saveAs(blob, 'GMU Schedule.ics');
};
const addToSystemCalendar = () => {
window.open(`webcal://${window.location.hostname}/api/schedules?section_ids=${window.cart._courses.join(',')}`);
};
const initListeners = () => {
document.getElementById('open-modal-btn').onclick = setUrlInModal;
document.getElementById('download-ics').onclick = downloadIcs;
document.getElementById('add-to-system').onclick = addToSystemCalendar;
document.getElementById('share-url').innerText = `${window.location.protocol}//${window.location.hostname}/schedule/view?section_ids=${window.cart._courses.join(',')}`;
};
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
/**
* Either adds or removes a section from the cart depending on
* if it is currently in the cart.
*/
const addOrRemoveFromCart = async (event, sectionNode) => {
event && event.stopPropagation();
const section = { ...sectionNode.dataset };
await this.cart.toggleSection(section);
const icon = $(sectionNode.querySelector('.add-remove-btn #icon'));
const text = sectionNode.querySelector('.add-remove-btn .text');
if (this.cart.includesSection(section)) {
icon.addClass('fa-minus').removeClass('fa-plus');
text.innerText = 'Remove';
} else {
icon.addClass('fa-plus').removeClass('fa-minus');
text.innerText = 'Add';
}
};
/**
* Toggles the display of the schedule
*/
const toggleSections = course => {
const sections = course.querySelector('.sections');
const chev = $(course.querySelector('#course-chevron'));
const label = course.querySelector('#chevron-label');
if (sections.style.display === 'flex') {
sections.style.display = 'none';
chev.addClass('fa-chevron-down').removeClass('fa-chevron-up');
label.innerText = 'Expand';
} else {
sections.style.display = 'flex';
chev.addClass('fa-chevron-up').removeClass('fa-chevron-down');
label.innerText = 'Minimize';
}
};
const initSearchListeners = () => {
const courseCards = Array.from(document.querySelectorAll('.course-card'));
courseCards.forEach(card => {
card.onclick = () => toggleSections(card);
});
const sectionItems = Array.from(document.querySelectorAll('.section-item'));
sectionItems.forEach(item => (item.onclick = event => addOrRemoveFromCart(event, item)));
};
document.addEventListener('DOMContentLoaded', initSearchListeners);
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
......@@ -10,6 +10,83 @@
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree .
* require_tree .
*= require cart
*= require navbar
*= require_self
*/
body {
background-color: #E4E4E4;
}
.card {
margin-bottom: 12px;
background-color: white;
border-radius: 8px;
box-shadow: 0 0 5px rgba(0,0,0,0.2);
transition: 0.3s;
.card-header {
display: flex;
flex-direction: column;
}
}
.attr-list {
display: flex;
flex-direction: row;
.attr {
.icon {
padding-right: 4px;
}
align-items: center;
display: inline-flex;
white-space: nowrap;
}
}
.unpadded {
padding: 0px;
}
/* On mouse-over, add a deeper shadow */
.card:hover {
box-shadow: 0 0 20px rgba(0,0,0,0.4);
}
.align-vertical {
display: flex;
align-items: center;
}
.align-left {
display: flex;
justify-content: flex-start;
align-items: center;
}
.align-center {
display: flex;
justify-content: center;
align-items: center;
}
.align-right {
display: flex;
justify-content: flex-end;
align-items: center;
}
.form-control:focus {
border-color: transparent;
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(0, 0, 255, 0.5);
}
.quick-add-header {
margin-top: 32px;
}
#cart {
display: none;
}
#cart-button {
color: black;
}
#cart-button:hover {
transition: 0.15s;
color: green;
}
// Place all the styles related to the Search controller here.
// Place all the styles related to the Courses controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.section-item.selected {
background-color: lightgreen;
}
.section-item:hover {
transition: 0.15s;
background-color: lightgray;
}
// Place all the styles related to the Home controller here.
// Place all the styles related to the home controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
// Place all the styles related to the Courses controller here.
// Place all the styles related to the instructors controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.center-vert {
display: flex;
justify-content: center;
align-items: center;
}
#navbar {
margin-top: 8px;
margin-bottom: 48px;
}
#logo {
font-size: 24pt;
color: black;
white-space: nowrap;
margin-right: 8px;
}
#semester-select {
min-width: 100px;
}
#cart-button {
margin-top: 24px;
}
#calendar {
background-color: white;
padding: 16px;
margin-bottom: 8px;
margin-top: 8px;
min-width: 800px;
}
.section-item.selected {
background-color: white;
}
.section-item.selected:hover {
background-color: red;
}
#share-header {
margin-top: 16px;
}
// Place all the styles related to the search controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.section-item:hover {
transition: 0.15s;
border-style: solid;
border-width: 2px;
border-color: lightgray;
}
// .section-item.selected {
// transition: 0.15s;
// background-color: lightgreen;
// }
// .section-item.selected:hover {
// transition: 0.15s;
// background-color: rgba(255, 0, 0, 0.6);
// }
class API::CourseListingsController < ApplicationController
resource_description do
short 'Working with courses and associated sections'
end
api :GET, '/course_listings', "Get all available courses and their sections"
param :subject, String, desc: 'Course subject, e.g. "CS" or "ACCT"'
param :number, Integer, desc: 'Course number, e.g. "112"'
def index
# Make a separate list so that we can include sections
@courses = API::CourseListingsHelper::CourseListing.wrap(Course.fetch(params).all)
render json: @courses
end
end
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