Skip to content
187 changes: 187 additions & 0 deletions src/web/src/components/CoursePageListing.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<template>
<b-list-group flush>
<b-list-group-item
class="selected"
v-for="section in sortedSections"
:key="section.crn"
@click.stop="toggleCourseSection(section)"
:style="{
'border-left': section.selected
? `4px solid ${getBorderColor(course.name)}`
: 'none',
'background-color': section.selected
? `${getBackgroundColor(course.name)} !important`
: $store.state.darkMode
? 'var(--dark-primary)'
: 'white',
color: section.selected
? 'black'
: $store.state.darkMode
? 'var(--dark-primary-text)'
: 'black',
}"
>
<b-row class="mb-2" align-h="between">
<b-col cols="auto">
<div class="mb-1 d-inline">
{{ section.crn }} - {{ section.sessions[0].section }} - {{ getInstructor(section.sessions) }}
</div>
<div class="d-inline" style="position: relative; top: -2px; margin-left: 10px;">
<course-section-seats-badge
v-if="section.seats_total > 0"
:seatsOpen="section.seats_open"
:seatsFilled="section.seats_filled"
:seatsTotal="section.seats_total"
/>
</div>
</b-col>
</b-row>

<span
v-for="courseSession in section.sessions"
:key="
courseSession.crn +
courseSession.day_of_week +
courseSession.time_start
"
>
{{ DAY_SHORTNAMES[courseSession.day_of_week + 1] }}:
{{ readableTime(courseSession.time_start) }} -
{{ readableTime(courseSession.time_end) }}
<br />
</span>
</b-list-group-item>
</b-list-group>
</template>

<script>
import "@/typedef";
import { DAY_SHORTNAMES, readableTime, readableDate } from "@/utils";
import { getBackgroundColor, getBorderColor } from "@/services/ColorService";
import {
faTimes,
faPlus,
faChevronDown,
faChevronUp,
} from "@fortawesome/free-solid-svg-icons";
import CourseSectionSeatsBadge from "./CourseSectionSeatsBadge.vue";
// Course Listing by default is a collapsible display of a course and its
// sections and sessions
// However, there are slots available to customize the information displayed
// So far there are two slots with the corresponding scoped props
// 1) toggleCollapseButton { course, toggleCollapse() }
// 2) collapseContent { course }
export default {
name: "CourseListing",
components: {
CourseSectionSeatsBadge,
},
props: {
course: Object,
// If true, collapse is open when created
// If lazyLoadCollapse is true, this is ignored
openInitial: {
type: Boolean,
default: false,
},
// If true, do not render/add collapse content to the DOM
// until collapse is opened
// default false
lazyLoadCollapse: {
type: Boolean,
default: false,
},
// Method name of default action
// When body of CourseListing is clicked on, the
// defaultAction is called
// Kind of hacky, doesnt allow parameters, but keeps it
// relatively flexible
defaultAction: {
type: String,
default: "toggleCollapse",
},
//if this is false the add course + button wont appear
//this is useful for the course explorer
showAddButton: {
type: Boolean,
default: true,
},
},
data() {
return {
faTimes,
faPlus,
faChevronDown,
faChevronUp,
DAY_SHORTNAMES,
// v-model with collapse
// true or false for open or close respectively collapse
showCollapse: !this.lazyLoadCollapse && this.openInitial,
// initially false, set to true on first collapse toggle
// Used for lazy loading
loaded: false,
showAdd: this.showAddButton,
};
},
methods: {
readableTime,
readableDate,
getBackgroundColor,
getBorderColor,
// Just a wrapper, can't call `[defaultAction]()` in html
callDefaultAction() {
this[this.defaultAction]();
},
/**
* Toggle collapse state
* @param {boolean} collapse If provided, set collapse state
*/
toggleCollapse(collapse) {
if (!this.loaded) {
this.loaded = true;
}
this.showCollapse =
collapse !== undefined ? collapse : !this.showCollapse;
},
/**
* Toggle use selection of course section
* If a user is clicking on course section for the first time,
* add course section to schedules
* If the course section had already been clicked
* remove course section from schedules
* @param {CourseSection} section
*/
toggleCourseSection(section) {
if (section.selected) {
this.$emit("removeCourseSection", section);
} else {
this.$emit("addCourseSection", this.course, section);
}
},
//used in the course explorer to show a courses info modal
showInfoModal() {
this.$emit("showCourseInfo", this.course);
},
getInstructor(sessions) {
for (let i = 0; i < sessions.length; i++) {
if (sessions[i].instructor !== "Staff") {
return sessions[i].instructor;
}
}
},
},
computed: {
sortedSections() {
return this.course.sections
.slice()
.sort((a, b) => a.sessions[0].section - b.sessions[0].section);
},
},
};
</script>

<style>
.click-me {
cursor: pointer;
}
</style>
69 changes: 51 additions & 18 deletions src/web/src/pages/CoursePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,46 @@
<b-row>
<b-col>
<h1 class="mt-4">{{ courseObj.title }}</h1>
<h4 class="mb-1 d-inline">{{ courseName }}</h4>
&nbsp;
<div class="d-inline">
<course-sections-open-badge :course="courseObj" />
</div>
<hr />
</b-col>
</b-row>
<b-row>
<b-col>
<h6 class="mb-1 d-inline">{{ getCredits }} Credits</h6>
</b-col>
</b-row>
<b-row>
<b-col>
<p v-html="transformed" />
<b-col cols="8" class="mb-4">
<b-row>
<b-col>
<h4 class="mb-1 d-inline">{{ courseName }}</h4>
</b-col>
</b-row>
<b-row>
<b-col>
<h6 class="mb-1 d-inline">{{ getCredits }} Credits</h6>
<div class="d-inline" style="position: relative; top: -2px; margin-left: 10px;">
<course-sections-open-badge :course="courseObj" />
</div>
</b-col>
</b-row>
<b-row>
<b-col>
<div class="prerequisites-container">
<p class="prerequisites-text">Prerequisites:</p>
<p class="prerequisites-link" v-html="transformed" />
</div>
</b-col>
</b-row>
<b-row>
<b-col>
<h5>Course Description:</h5>
{{ courseObj.description }}
</b-col>
</b-row>
</b-col>
</b-row>
<b-row>
<b-col class="mb-4">
<b-col cols="4" class="mb-4">
<br />
{{ courseObj.description }}
<h5>Open Sections:</h5>
<CoursePageListing :course="courseObj" v-on="$listeners"/>
</b-col>
</b-row>
<b-button @click="$router.go(-1)">Back</b-button>
<b-button @click="$router.go(-1)" class="mt-3">Back</b-button>
<!-- :to="'/explore/' + courseObj.department"-->
</div>
<CenterSpinner
Expand Down Expand Up @@ -60,11 +76,13 @@ import { COURSES } from "@/store";
import { generateRequirementsText } from "@/utils";
import CenterSpinnerComponent from "../components/CenterSpinner.vue";
import CourseSectionsOpenBadge from "../components/CourseSectionsOpenBadge.vue";
import CoursePageListingComponent from "@/components/CoursePageListing";

export default {
components: {
CenterSpinner: CenterSpinnerComponent,
CourseSectionsOpenBadge,
CoursePageListing: CoursePageListingComponent,
},
name: "CoursePage",
data() {
Expand Down Expand Up @@ -139,7 +157,7 @@ export default {
},
metaInfo() {
return {
title: this.courseObj.name,
title: this.courseObj ? this.courseObj.name : '',
titleTemplate: "%s | YACS",
meta: !this.courseObj
? undefined
Expand All @@ -165,5 +183,20 @@ export default {
],
};
},
mounted() {
console.log(this.courseObj);
},

};
</script>

<style>
.prerequisites-container {
display: flex;
align-items: center; /* Optional: Aligns items vertically in the center */
}

.prerequisites-text {
margin-right: 10px; /* Optional: Adds some space between the text and the link */
}
</style>