Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
SRCT
schedules
Commits
d213fe83
Commit
d213fe83
authored
Jul 14, 2019
by
Zac Wood
Browse files
Merge branch 'dev-v3' into 'master'
Version 3 See merge request
!51
parents
b6309e4e
80cf9394
Pipeline
#4473
passed with stage
in 2 minutes and 47 seconds
Changes
96
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
schedules/app/javascript/src/CourseList.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
Course
from
'
src/Course
'
;
export
default
class
CourseList
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
}
render
()
{
return
(
<
div
>
{
this
.
props
.
courses
.
map
(
course
=>
(
<
Course
key
=
{
course
.
id
}
{
...
course
}
/>
))
}
</
div
>
);
}
}
schedules/app/javascript/src/ExportModal.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
Cart
from
'
src/Cart
'
;
import
{
saveAs
}
from
'
file-saver
'
;
export
default
class
extends
React
.
Component
{
render
()
{
return
(
<
div
className
=
"modal fade"
id
=
"exportModal"
tabindex
=
"-1"
role
=
"dialog"
aria
-
labelledby
=
"exportModalLabel"
aria
-
hidden
=
"true"
>
<
div
className
=
"modal-dialog"
role
=
"document"
>
<
div
className
=
"modal-content"
>
<
div
className
=
"modal-header"
>
<
h5
className
=
"modal-title"
id
=
"exportModalLabel"
>
Your calendar has been generated!
<
br
/>
(Click on the options below to see further
instructions)
</
h5
>
<
button
type
=
"button"
className
=
"close"
data
-
dismiss
=
"modal"
aria
-
label
=
"Close"
>
<
span
aria
-
hidden
=
"true"
>
×
</
span
>
</
button
>
</
div
>
<
div
className
=
"modal-body"
>
<
button
type
=
"button"
className
=
"btn-variant"
data
-
toggle
=
"collapse"
data
-
target
=
"#apple-info"
>
{
'
'
}
<
h5
>
Apple Calendar
</
h5
>
{
'
'
}
</
button
>
<
div
id
=
"apple-info"
className
=
"collapse"
>
To add your schedule to Apple Calendar, click the "Add to calendar" button below. If you
are on a device running macOS or iOS, this will open a dialogue which will walk you
through adding the calendar.
</
div
>
<
hr
/>
<
button
type
=
"button"
className
=
"btn-variant"
data
-
toggle
=
"collapse"
data
-
target
=
"#google-info"
>
{
'
'
}
<
h5
>
Google Calendar
</
h5
>
{
'
'
}
</
button
>
<
div
id
=
"google-info"
className
=
"collapse"
>
<
strong
>
On desktop:
</
strong
>
<
br
/>
First, download the calendar file using the "Download calendar file" below. Open your
{
'
'
}
<
a
href
=
"https://calendar.google.com/"
target
=
"_blank"
>
Google Calendar
</
a
>
. Click the "Settings" button in the top right, and then click the Settings tab. In the
menu on the left, click "Import
&
export" and "Import". Now, upload the calendar file
you downloaded and click "Import".
<
br
/>
<
strong
>
On mobile (Android only):
</
strong
>
<
br
/>
Click the "Download calendar file" button. This will download the calendar file which
you may then open and add to your calendar.
<
br
/>
</
div
>
<
hr
/>
<
button
type
=
"button"
className
=
"btn-variant"
data
-
toggle
=
"collapse"
data
-
target
=
"#outlook-info"
>
{
'
'
}
<
h5
>
Outlook Calendar
</
h5
>
{
'
'
}
</
button
>
<
div
id
=
"outlook-info"
className
=
"collapse"
>
<
button
type
=
"button"
className
=
"btn-variant"
data
-
toggle
=
"collapse"
data
-
target
=
"#outlook-desktop"
>
{
'
'
}
<
strong
>
On desktop (Windows):
</
strong
>
{
'
'
}
</
button
>
<
br
/>
<
div
id
=
"outlook-desktop"
className
=
"collapse"
>
First, download the calendar file using the “Download calendar file” button below.
In Outlook, choose File, then Open and Export, and then Import/Export. In the Import
and Export Wizard Box, choose “Import and iCalendar (.ics) or vCalendar file (.vcs)”
and the “next” button. Search for the button you downloaded in the beginning. Click
“Okay” and then “Import.”
</
div
>
<
br
/>
<
button
type
=
"button"
className
=
"btn-variant"
data
-
toggle
=
"collapse"
data
-
target
=
"#outlook-mac"
>
{
'
'
}
<
strong
>
On desktop (Mac):
</
strong
>
{
'
'
}
</
button
>
<
br
/>
<
div
id
=
"outlook-mac"
className
=
"collapse"
>
First, download the calendar file using the “Download calendar file” button below.
Open Outlook and make sure the calendar in which you want to import the file into
has a checkmark next to it. Alternatively, you can add it into a new calendar by
clicking the “Organize” tab and then the “New Calendar” button. Double click the new
Calendar to rename it. Open the Finder application and search for the file you
downloaded in the beginning. Then, drag and drop the file into the desired Calendar
area.
</
div
>
<
br
/>
<
button
type
=
"button"
className
=
"btn-variant"
data
-
toggle
=
"collapse"
data
-
target
=
"#outlook-classNameic"
>
{
'
'
}
<
strong
>
Outlook Online (ClassNameic Layout)
</
strong
>
{
'
'
}
</
button
>
<
br
/>
<
div
id
=
"outlook-classNameic"
className
=
"collapse"
>
To check if you are using the ClassNameic Layout, look in the top right and see if
“The new Outlook” bar is slid to the left. If it is not, you may consider reading
“The New Outlook Layout” instructions or clicking the bar to slide it to the left.
First, download the calendar file using the “Download calendar file” button below.
Login onto your
{
'
'
}
<
a
href
=
"https://outlook.live.com/owa/"
target
=
"blank"
>
Outlook
</
a
>
{
'
'
}
and click the calendar icon on the bottom left. On the menu bar, located above the
Calendar, choose the “Add Calendar” menu. From the drop down menu, click import from
file and browse for the calendar file you downloaded in the beginning. Click the
save icon, then the calendar will be imported.
</
div
>
<
br
/>
<
button
type
=
"button"
className
=
"btn-variant"
data
-
toggle
=
"collapse"
data
-
target
=
"#outlook-new"
>
{
'
'
}
<
strong
>
Outlook Online (New Outlook Layout)
</
strong
>
{
'
'
}
</
button
>
<
br
/>
<
div
id
=
"outlook-new"
className
=
"collapse"
>
To check if you are using the New Outlook Layout, look in the top right and see if
“The new Outlook” bar is slid to the right. If it is not, you may consider reading
the “ClassNameic Layout” instructions or clicking the bar to slide it to the right.
First download the calendar file using the “Download calendar file” button below.
Login onto your
{
'
'
}
<
a
href
=
"https://outlook.live.com/owa/"
target
=
"blank"
>
Outlook
</
a
>
{
'
'
}
and click the calendar icon on the bottom left. On the left side bar, under
“Calendars”, click the “Discover calendars” button. Choose on the “From File” menu
under the “Import” Section. Then click the browse button and search for the file you
downloaded in the beginning. Lastly, choose “Import” and your calendar will be
displayed.
</
div
>
</
div
>
<
br
/>
<
hr
/>
<
h5
>
.ics file
</
h5
>
To download a .ics file containing your schedule, click the "Download calendar file" button
below.
</
div
>
<
div
className
=
"modal-footer flex"
>
<
button
id
=
"download-ics"
type
=
"button"
className
=
"btn btn-secondary"
onClick
=
{
this
.
downloadIcs
}
>
Download calendar file
</
button
>
<
button
id
=
"add-to-system"
type
=
"button"
className
=
"btn btn-primary"
onClick
=
{
this
.
addToSystemCalendar
}
>
Add to system calendar
</
button
>
</
div
>
</
div
>
</
div
>
</
div
>
);
}
downloadIcs
=
async
()
=>
{
const
response
=
await
fetch
(
`
${
window
.
location
.
protocol
}
//
${
window
.
location
.
hostname
}${
window
.
location
.
port
===
'
3000
'
?
'
:3000
'
:
''
}
/api/schedules?crns=
${
Cart
.
crns
.
join
(
'
,
'
)}
`
);
const
text
=
await
response
.
text
();
const
blob
=
new
Blob
([
text
],
{
type
:
'
text/calendar;charset=utf-8
'
});
saveAs
(
blob
,
'
GMU Schedule.ics
'
);
};
addToSystemCalendar
=
()
=>
{
window
.
open
(
`webcal://
${
window
.
location
.
hostname
}${
window
.
location
.
port
===
'
3000
'
?
'
:3000
'
:
''
}
/api/schedules?crns=
${
Cart
.
crns
.
join
(
'
,
'
)}
`
);
};
}
schedules/app/javascript/src/InstructorCard.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
export
default
class
InstructorCard
extends
React
.
Component
{
render
()
{
const
inst
=
this
.
props
.
instructor
;
return
(
<
div
className
=
"card p-3"
>
<
span
>
<
i
className
=
"fas fa-chalkboard-teacher mr-2"
/>
<
a
href
=
{
`/instructors/
${
inst
.
id
}
`
}
>
{
this
.
props
.
instructor
.
name
}
</
a
>
</
span
>
</
div
>
);
}
}
schedules/app/javascript/src/InstructorList.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
InstructorCard
from
'
src/InstructorCard
'
;
export
default
class
InstructorList
extends
React
.
Component
{
render
()
{
return
<
div
>
{
this
.
props
.
instructors
&&
this
.
props
.
instructors
.
map
(
i
=>
<
InstructorCard
instructor
=
{
i
}
/>)
}
</
div
>;
}
}
schedules/app/javascript/src/QuickAdd.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
Cart
from
'
src/Cart
'
;
export
default
class
QuickAdd
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
crnString
:
''
};
}
add
=
e
=>
{
e
.
preventDefault
();
const
crns
=
this
.
state
.
crnString
.
split
(
'
,
'
);
crns
.
forEach
(
c
=>
c
.
length
===
5
&&
Cart
.
addCrn
(
c
));
this
.
props
.
loadCalendar
();
};
render
()
{
return
(
<
div
>
<
h3
className
=
"quick-add-header"
>
Quick add
</
h3
>
<
p
>
Want to quickly generate a calendar populated with your semester's classes? Enter the CRNs in a comma separated list below.
</
p
>
<
form
onSubmit
=
{
this
.
add
}
className
=
"form"
>
<
div
className
=
"input-group"
>
<
input
id
=
"crns"
name
=
"crns"
type
=
"text"
value
=
{
this
.
state
.
crns
}
onChange
=
{
e
=>
this
.
setState
({
crnString
:
e
.
target
.
value
})
}
className
=
"form-control"
placeholder
=
"12345,54321,..."
aria
-
describedby
=
"basic-addon2"
autoComplete
=
"off"
/>
<
div
className
=
"input-group-append"
>
<
button
type
=
"submit"
className
=
"btn btn-primary"
type
=
"button"
>
Populate Calendar
</
button
>
</
div
>
</
div
>
</
form
>
</
div
>
);
}
}
schedules/app/javascript/src/SearchList.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
CourseList
from
'
src/CourseList
'
;
import
InstructorList
from
'
src/InstructorList
'
;
export
default
class
SearchList
extends
React
.
Component
{
render
()
{
return
(
<
div
>
<
InstructorList
instructors
=
{
this
.
props
.
instructors
}
/>
<
CourseList
courses
=
{
this
.
props
.
courses
}
/>
</
div
>
);
}
}
schedules/app/javascript/src/Section.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
Cart
from
'
src/Cart
'
;
import
Stars
from
'
src/Stars
'
;
export
default
class
Section
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
inCart
:
Cart
.
includesCrn
(
this
.
props
.
crn
)
};
}
onClick
=
e
=>
{
e
.
stopPropagation
();
if
(
this
.
props
.
readOnly
)
return
;
console
.
log
(
e
.
target
.
tagName
);
if
(
e
.
target
.
tagName
===
'
A
'
)
return
;
// if we clicked on a link, don't add the section to the cart
Cart
.
toggleCrn
(
this
.
props
.
crn
);
this
.
setState
({
inCart
:
Cart
.
includesCrn
(
this
.
props
.
crn
)
});
this
.
props
.
onClick
&&
this
.
props
.
onClick
(
this
.
props
.
crn
);
};
render
()
{
const
{
name
,
title
,
crn
,
readOnly
,
instructor_name
,
instructor_url
,
teaching_rating
,
location
,
days
,
start_time
,
end_time
,
}
=
this
.
props
;
const
{
inCart
}
=
this
.
state
;
const
percent
=
teaching_rating
?
(
<
Stars
percent
=
{
(
teaching_rating
[
0
]
/
5
)
*
100
}
/>
)
:
null
;
const
remove
=
(
<
span
className
=
"float-right text-center add-remove-btn"
style
=
{
inCart
?
{}
:
{
display
:
'
none
'
}
}
>
<
i
id
=
"icon"
className
=
"fas fa-minus"
/>
<
br
/>
<
span
className
=
"text"
>
Remove
</
span
>
</
span
>
);
const
add
=
(
<
span
className
=
"float-right text-center add-remove-btn"
style
=
{
inCart
?
{
display
:
'
none
'
}
:
{}
}
>
<
i
id
=
"icon"
className
=
"fas fa-plus"
/>
<
br
/>
<
span
className
=
"text"
>
Add
</
span
>
</
span
>
);
return
(
<
li
className
=
"list-group-item section-item"
onClick
=
{
this
.
onClick
}
>
<
p
>
<
b
>
{
name
}
</
b
>
:
{
title
}{
'
'
}
<
em
>
(#
{
crn
}
)
</
em
>
</
p
>
{
!
readOnly
&&
(
<
div
>
{
remove
}
{
add
}
</
div
>
)
}
<
i
className
=
"fas fa-chalkboard-teacher"
/>
{
'
'
}
{
instructor_name
!==
'
TBA
'
?
(
<
span
>
<
a
href
=
{
instructor_url
}
>
{
instructor_name
}
</
a
>
{
percent
}
</
span
>
)
:
(
<
span
>
{
instructor_name
}
</
span
>
)
}
<
br
/>
<
i
className
=
"fas fa-map-marker-alt"
/>
{
location
}
<
br
/>
<
i
className
=
"fas fa-clock"
/>
{
days
}
,
{
start_time
}
-
{
end_time
}
<
br
/>
</
li
>
);
}
}
schedules/app/javascript/src/SectionList.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
Chevron
from
'
src/Chevron
'
;
import
Section
from
'
src/Section
'
;
export
default
class
SectionList
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
}
render
()
{
return
(
<
div
>
{
this
.
props
.
expandable
?
<
Chevron
open
=
{
this
.
props
.
expanded
}
/>
:
null
}
{
this
.
props
.
expanded
?
(
<
div
className
=
"d-flex list-group list-group-flush sections"
>
{
this
.
props
.
sections
.
map
(
section
=>
(
<
Section
key
=
{
section
.
id
}
onClick
=
{
this
.
props
.
onClick
}
readOnly
=
{
this
.
props
.
readOnly
}
{
...
section
}
/>
))
}
</
div
>
)
:
(
<
div
/>
)
}
</
div
>
);
}
}
schedules/app/javascript/src/ShareModal.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
const
ShareModal
=
props
=>
{
return
(
<
div
className
=
"modal fade"
id
=
"shareModal"
tabindex
=
"-1"
role
=
"dialog"
aria
-
labelledby
=
"shareModalLabel"
aria
-
hidden
=
"true"
>
<
div
className
=
"modal-dialog"
role
=
"document"
>
<
div
className
=
"modal-content"
>
<
div
className
=
"modal-header"
>
<
h5
className
=
"modal-title"
>
Share your schedule!
</
h5
>
</
div
>
<
div
className
=
"modal-body"
>
The following link contains a copy of your schedule:
<
br
/>
<
a
id
=
"shareLink"
href
=
{
props
.
link
}
>
{
props
.
link
}
</
a
>
</
div
>
</
div
>
</
div
>
</
div
>
);
};
export
default
ShareModal
;
schedules/app/javascript/src/Stars.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
export
default
class
Stars
extends
React
.
Component
{
render
()
{
return
(
<
div
className
=
"star-rating"
>
<
div
className
=
"back-stars"
>
<
i
className
=
"fas fa-star"
aria
-
hidden
=
"true"
/>
<
i
className
=
"fas fa-star"
aria
-
hidden
=
"true"
/>
<
i
className
=
"fas fa-star"
aria
-
hidden
=
"true"
/>
<
i
className
=
"fas fa-star"
aria
-
hidden
=
"true"
/>
<
i
className
=
"fas fa-star"
aria
-
hidden
=
"true"
/>
<
div
className
=
"front-stars"
style
=
{
{
width
:
`
${
this
.
props
.
percent
}
%`
}
}
>
<
i
className
=
"fa fa-star"
aria
-
hidden
=
"true"
/>
<
i
className
=
"fa fa-star"
aria
-
hidden
=
"true"
/>
<
i
className
=
"fa fa-star"
aria
-
hidden
=
"true"
/>
<
i
className
=
"fa fa-star"
aria
-
hidden
=
"true"
/>
<
i
className
=
"fa fa-star"
aria
-
hidden
=
"true"
/>
</
div
>
</
div
>
</
div
>
);
}
}
schedules/app/javascript/src/Toolbar.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
BigCalendar
from
'
react-big-calendar
'
;
import
Toolbar
from
'
react-big-calendar/lib/Toolbar
'
;
import
'
!style-loader!css-loader!react-big-calendar/lib/css/react-big-calendar.css
'
;
import
withSizes
from
'
react-sizes
'
;
class
CustomToolbar
extends
Toolbar
{
render
()
{
const
{
label
,
isMobile
}
=
this
.
props
;
if
(
isMobile
&&
label
===
''
)
{
this
.
view
(
'
day
'
);
}
return
(
<
div
className
=
"rbc-toolbar d-flex justify-content-between"
>
{
!
isMobile
&&
(
<
div
>
<
span
className
=
"rbc-btn-group"
>
<
button
type
=
"button"
onClick
=
{
()
=>
this
.
view
(
'
day
'
)
}
>
Day
</
button
>
<
button
type
=
"button"
onClick
=
{
()
=>
this
.
view
(
'
week
'
)
}
>
Week
</
button
>
</
span
>
</
div
>
)
}
<
span
className
=
"rbc-toolbar-label"
>
{
this
.
props
.
label
}
</
span
>
{
this
.
props
.
view
===
'
day
'
&&
(
<
span
className
=
"rbc-btn-group"
>
{
this
.
props
.
label
!==
'
Sun
'
&&
(
<
button
type
=
"button"
onClick
=
{
()
=>
this
.
navigate
(
'
PREV
'
)
}
>
Back
</
button
>
)
}
{
this
.
props
.
label
!==
'
Sat
'
&&
(
<
button
type
=
"button"
onClick
=
{
()
=>
this
.
navigate
(
'
NEXT
'
)
}
>
Next
</
button
>
)
}
</
span
>
)
}
</
div
>
);
}
navigate
=
action
=>
{
console
.
log
(
action
);
this
.
props
.
onNavigate
(
action
);
};
view
=
action
=>
{
this
.
props
.
onView
(
action
);
};
}
const
mapSizesToProps
=
({
width
})
=>
({
isMobile
:
width
<
1000
,
});
export
default
withSizes
(
mapSizesToProps
)(
CustomToolbar
);
schedules/app/javascript/src/ViewCalendarPage.jsx
0 → 100644
View file @
d213fe83
import
React
from
'
react
'
;
import
Calendar
from
'
src/Calendar
'
;
import
Cart
from
'
src/Cart
'
;
import
SectionList
from
'
src/SectionList
'
;
import
ExportModal
from
'
src/ExportModal
'
;
import
QuickAdd
from
'
src/QuickAdd
'
;
import
moment
from
'
moment
'
;
import
'
url-polyfill
'
;
const
params
=
new
URLSearchParams
(
document
.
location
.
search
);
const
crns
=
params
.
get
(
'
crns
'
);
export
default
class
CalendarPage
extends
React
.
Component
{
state
=
{
events
:
[],
sections
:
[]
};
constructor
(
props
)
{
super
(
props
);
this
.
loadEvents
();
}
loadEvents
=
async
()
=>
{
const
response
=
await
fetch
(
`/schedule/events?crns=
${
crns
}
`
);
const
json
=
await
response
.
json
();
this
.
setState
({
...
json
});
};