Skip to content

Commit 677a4c9

Browse files
authored
Merge pull request #55 from GoGaucho/planner-fix
fix the planner issue in Planner.vue and format Course.vue(Shift+Alt+F)
2 parents 843bee3 + 1d568bd commit 677a4c9

2 files changed

Lines changed: 150 additions & 64 deletions

File tree

src/components/Course.vue

Lines changed: 114 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,104 @@
11
<script setup>
2-
import { watch } from 'vue'
3-
import { CheckIcon } from '@heroicons/vue/24/outline'
4-
import { get } from '../firebase.js'
5-
import state from '../model.js'
6-
import Wrapper from './Wrapper.vue'
7-
import Instructor from './Instructor.vue'
8-
import LabelSwitch from './LabelSwitch.vue'
2+
import { watch } from "vue";
3+
import { CheckIcon } from "@heroicons/vue/24/outline";
4+
import { get } from "../firebase.js";
5+
import state from "../model.js";
6+
import Wrapper from "./Wrapper.vue";
7+
import Instructor from "./Instructor.vue";
8+
import LabelSwitch from "./LabelSwitch.vue";
99
1010
const props = defineProps({
1111
modelValue: String,
1212
quarter: {
1313
type: String,
1414
default: () => state.course.quarter,
15-
}
16-
})
17-
const emits = defineEmits(['update:modelValue'])
15+
},
16+
});
17+
const emits = defineEmits(["update:modelValue"]);
1818
1919
// control UI
20-
const levels = { U: 'Undergraduate', G: 'Graduate', L: 'Lower Division', S: 'Upper Division' }
21-
const gradings = { null: 'optional', L: 'Letter', P: 'P/NP' }
20+
const levels = {
21+
U: "Undergraduate",
22+
G: "Graduate",
23+
L: "Lower Division",
24+
S: "Upper Division",
25+
};
26+
const gradings = { null: "optional", L: "Letter", P: "P/NP" };
2227
23-
let course = $ref({}), isSummer = $ref(false)
24-
let title = $computed(() => props.modelValue ? props.modelValue + ': ' + (course.title || '') : 'Please select a course')
28+
let course = $ref({}),
29+
isSummer = $ref(false);
30+
let title = $computed(() =>
31+
props.modelValue
32+
? props.modelValue + ": " + (course.title || "")
33+
: "Please select a course",
34+
);
2535
26-
let instructorName = $ref('')
27-
function leaveInstructor () {
28-
if (!state.screen.md) instructorName = ''
36+
let instructorName = $ref("");
37+
function leaveInstructor() {
38+
if (!state.screen.md) instructorName = "";
2939
}
3040
3141
watch(
3242
() => [props.modelValue, props.quarter],
3343
async ([v, q]) => {
3444
if (!v) {
35-
course = {}
36-
isSummer = false
37-
return
45+
course = {};
46+
isSummer = false;
47+
return;
3848
}
3949
40-
course = {}
41-
isSummer = false
42-
const quarter = q || state.course.quarter
50+
course = {};
51+
isSummer = false;
52+
const quarter = q || state.course.quarter;
4353
44-
course = await get('course/' + quarter + v)
54+
course = await get("course/" + quarter + v);
4555
4656
for (const s in course.sections) {
4757
if (course.sections[s].session) {
48-
isSummer = true
49-
break
58+
isSummer = true;
59+
break;
5060
}
5161
}
52-
setTimeout(() => { course.show = 1 })
62+
setTimeout(() => {
63+
course.show = 1;
64+
});
5365
},
54-
{ immediate: true }
55-
)
56-
57-
58-
function toggleFocus () {
59-
const k = props.modelValue
60-
if (state.course.focus[k]) delete state.course.focus[k]
61-
else state.course.focus[k] = 1
66+
{ immediate: true },
67+
);
68+
// the Focus button on each course
69+
function toggleFocus() {
70+
const k = props.modelValue;
71+
if (state.course.focus[k]) delete state.course.focus[k];
72+
else state.course.focus[k] = 1;
6273
}
6374
</script>
6475

6576
<template>
6677
<Transition name="fade">
67-
<div v-if="state.screen.md && props.modelValue" @click="emits('update:modelValue', false)" class="fixed w-full h-screen bg-black opacity-30 z-50 top-0" />
78+
<div
79+
v-if="state.screen.md && props.modelValue"
80+
@click="emits('update:modelValue', false)"
81+
class="fixed w-full h-screen bg-black opacity-30 z-50 top-0"
82+
/>
6883
</Transition>
69-
<div class="course bg-white p-4 sm:p-6 flex-grow all-transition
70-
fixed top-0 h-screen w-5/6 sm:w-11/12 z-50 overflow-auto
71-
md:sticky md:top-20 md:w-auto md:h-auto md:shadow-md md:z-10
72-
" :style="{ left: state.screen.md && !props.modelValue ? '-100%' : '0px' }">
84+
<div
85+
class="course bg-white p-4 sm:p-6 flex-grow all-transition fixed top-0 h-screen w-5/6 sm:w-11/12 z-50 overflow-auto md:sticky md:top-20 md:w-auto md:h-auto md:shadow-md md:z-10"
86+
:style="{ left: state.screen.md && !props.modelValue ? '-100%' : '0px' }"
87+
>
7388
<h2 class="text-xl sm:text-2xl font-bold mr-6">{{ title }}</h2>
7489
<Wrapper :show="course.show" :key="props.modelValue">
75-
<p class="text-sm text-gray-600">{{ course.college }} &nbsp; {{ course.department }} &nbsp; {{ levels[course.level] }}</p>
76-
<button class="text-white text-sm shadow rounded-full bg-blue-500 px-4 py-1 my-2 flex items-center" @click="toggleFocus">
77-
<CheckIcon class="all-transition overflow-x-hidden" :class="state.course.focus[props.modelValue] ? 'w-5 mr-1' : 'w-0'" />
90+
<p class="text-sm text-gray-600">
91+
{{ course.college }} &nbsp; {{ course.department }} &nbsp;
92+
{{ levels[course.level] }}
93+
</p>
94+
<button
95+
class="text-white text-sm shadow rounded-full bg-blue-500 px-4 py-1 my-2 flex items-center"
96+
@click="toggleFocus"
97+
>
98+
<CheckIcon
99+
class="all-transition overflow-x-hidden"
100+
:class="state.course.focus[props.modelValue] ? 'w-5 mr-1' : 'w-0'"
101+
/>
78102
Focus
79103
</button>
80104
<p class="my-2">{{ course.description }}</p>
@@ -86,9 +110,13 @@ function toggleFocus () {
86110
<LabelSwitch v-for="g in course.GE">{{ g }}</LabelSwitch>
87111
<span v-if="!course.GE.length" class="px-1">N/A</span>
88112
</p>
89-
<hr class="my-3">
113+
<hr class="my-3" />
90114
<div class="w-full overflow-x-auto">
91-
<table v-if="course.tree" class="w-full text-center" :style="{ minWidth: isSummer ? '560px' : '480px' }">
115+
<table
116+
v-if="course.tree"
117+
class="w-full text-center"
118+
:style="{ minWidth: isSummer ? '560px' : '480px' }"
119+
>
92120
<tr>
93121
<th v-if="isSummer">Session</th>
94122
<th>Code</th>
@@ -97,19 +125,52 @@ function toggleFocus () {
97125
<th>Location</th>
98126
</tr>
99127
<template v-for="(ss, lec) in course.tree">
100-
<tr class="bg-blue-100 border-white border-y-1" :set="s = course.sections[lec]"><!-- lecture -->
128+
<tr
129+
class="bg-blue-100 border-white border-y-1"
130+
:set="s = course.sections[lec]"
131+
>
132+
<!-- lecture -->
101133
<td class="text-sm" v-if="isSummer">{{ s.session }}</td>
102134
<td :class="s.closed && 'line-through'">{{ lec }}</td>
103-
<td><div v-for="i in s.instructors" @click="instructorName = i" @mouseenter="instructorName = i" @mouseleave="leaveInstructor">{{ i }}</div></td>
104-
<td><div v-for="p in s.periods">{{ p.time }}</div></td>
105-
<td><div v-for="p in s.periods">{{ p.location }}</div></td>
135+
<td>
136+
<div
137+
v-for="i in s.instructors"
138+
@click="instructorName = i"
139+
@mouseenter="instructorName = i"
140+
@mouseleave="leaveInstructor"
141+
>
142+
{{ i }}
143+
</div>
144+
</td>
145+
<td>
146+
<div v-for="p in s.periods">{{ p.time }}</div>
147+
</td>
148+
<td>
149+
<div v-for="p in s.periods">{{ p.location }}</div>
150+
</td>
106151
</tr>
107-
<tr class="opacity-60 bg-blue-100 border-white border border-x-0 all-transition" v-for="code in ss" :set="s = course.sections[code]">
152+
<tr
153+
class="opacity-60 bg-blue-100 border-white border border-x-0 all-transition"
154+
v-for="code in ss"
155+
:set="s = course.sections[code]"
156+
>
108157
<td class="text-sm" v-if="isSummer">{{ s.session }}</td>
109158
<td :class="s.closed && 'line-through'">{{ code }}</td>
110-
<td><div v-for="i in s.instructors" @mouseenter="instructorName = i" @mouseleave="instructorName = ''">{{ i }}</div></td>
111-
<td><div v-for="p in s.periods">{{ p.time }}</div></td>
112-
<td><div v-for="p in s.periods">{{ p.location }}</div></td>
159+
<td>
160+
<div
161+
v-for="i in s.instructors"
162+
@mouseenter="instructorName = i"
163+
@mouseleave="instructorName = ''"
164+
>
165+
{{ i }}
166+
</div>
167+
</td>
168+
<td>
169+
<div v-for="p in s.periods">{{ p.time }}</div>
170+
</td>
171+
<td>
172+
<div v-for="p in s.periods">{{ p.location }}</div>
173+
</td>
113174
</tr>
114175
</template>
115176
</table>

src/views/Planner.vue

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,52 @@
11
<script setup>
2+
import { onActivated } from 'vue'
23
import { ArrowLeftIcon, CpuChipIcon, InformationCircleIcon } from '@heroicons/vue/24/outline'
34
import { get, log } from '../firebase.js'
45
import state from '../model.js'
56
import PanelWrapper from '../components/PanelWrapper.vue'
67
import Schedule from '../components/Schedule.vue'
78
import Instructor from '../components/Instructor.vue'
89
import { useRouter } from 'vue-router'
9-
const router = useRouter(), focus = state.course.focus
10+
const router = useRouter()
1011
log('web/planner')
1112
1213
let loading = $ref(true), choices = $ref({}), chosenwTime = $ref([])
14+
let focus = $ref({})
1315
let isSummer = $computed(() => state.course?.quarter && state.course.quarter[4] === '3')
1416
1517
let instructorName = $ref('')
1618
function leaveInstructor () {
1719
if (!state.screen.md) instructorName = ''
1820
}
1921
20-
const sections = {}, courses = [], conflicts = {}
22+
let sections = $ref({}), courses = $ref([]), conflicts = $ref({})
23+
2124
async function fetchData () {
22-
if (!focus) return router.push('/course')
23-
for (const k in focus) if (!focus[k]) delete focus[k]
24-
if (!Object.keys(focus).length) return router.push('/course')
25-
for (const k in focus) {
26-
const c = focus[k] = await get('course/' + state.course.quarter + k)
25+
// read and filter course from the state.course.focus
26+
const focusKeys = Object.keys(state.course.focus || {}).filter(k => state.course.focus[k])
27+
// if no course focused, return user to the course page
28+
if (!focusKeys.length) return router.push('/course')
29+
30+
const prevChoices = choices || {}
31+
//reset the local
32+
focus = {}
33+
choices = {}
34+
chosenwTime = []
35+
sections = {}
36+
courses = []
37+
conflicts = {}
38+
39+
for (const k of focusKeys) {
40+
const c = await get('course/' + state.course.quarter + k)
2741
focus[k] = c
28-
choices[k] = {}
42+
// if the user already made selections for this course before reload, restore them. Otherwise start with an empty selection.
43+
choices[k] = prevChoices[k] ? { ...prevChoices[k] } : {}
2944
courses.push(k)
3045
for (const s in c.sections) {
3146
sections[s] = c.sections[s]
32-
for (const p of sections[s].periods) p.wTime = JSON.parse(p.wTime)
47+
for (const p of sections[s].periods) {
48+
if (typeof p.wTime === 'string') p.wTime = JSON.parse(p.wTime)
49+
}
3350
}
3451
}
3552
const isOverlap = (x, y) => x[1] >= y[0] && x[0] <= y[1]
@@ -53,7 +70,13 @@ async function fetchData () {
5370
computeStatus()
5471
loading = false
5572
}
56-
fetchData()
73+
// When user changes selected courses and returns to Planner, re-fetch and rebuild data on activation
74+
async function init () {
75+
loading = true
76+
await fetchData()
77+
}
78+
79+
onActivated(init)
5780
5881
function isConflict (_choices, s, exception = '') {
5982
for (const k in _choices) {
@@ -141,6 +164,8 @@ let pieces = $computed(() => {
141164
})
142165
143166
function auto () {
167+
// ensure statuses are up-to-date before starting auto selection
168+
computeStatus()
144169
function scan () {
145170
for (const k in choices) {
146171
const c = choices[k]
@@ -196,7 +221,7 @@ function help () {
196221
<div class="flex items-start overflow-x-auto" style="min-height: 70vh;">
197222
<div class="overflow-x-auto shadow-md" :style="{ minWidth: isSummer ? '560px' : '480px' }">
198223
<!-- course list -->
199-
<PanelWrapper v-if="!loading" v-for="(v, k) in focus" :title="k" :class="choices[k]?.done ? 'bg-blue-100' : 'bg-gray-100'">
224+
<PanelWrapper v-if="!loading" v-for="(v, k) in focus" :key="k" :title="k" :class="choices[k]?.done ? 'bg-blue-100' : 'bg-gray-100'">
200225
<table v-if="v.tree" class="w-full text-center">
201226
<tr>
202227
<th v-if="isSummer">Session</th>

0 commit comments

Comments
 (0)