diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/App.vue | 21 | ||||
| -rw-r--r-- | src/components/EditorLists.vue | 96 | ||||
| -rw-r--r-- | src/features/parser.ts (renamed from src/features/parser.js) | 49 | ||||
| -rw-r--r-- | src/main.ts (renamed from src/main.js) | 0 | ||||
| -rw-r--r-- | src/models/EventInfo.ts (renamed from src/models/EventInfo.js) | 17 | ||||
| -rw-r--r-- | src/models/Lab.ts (renamed from src/models/Lab.js) | 6 | ||||
| -rw-r--r-- | src/models/PeerTeacher.ts (renamed from src/models/PeerTeacher.js) | 14 | ||||
| -rw-r--r-- | src/router/index.ts (renamed from src/router/index.js) | 0 | ||||
| -rw-r--r-- | src/shims-vue.d.ts | 6 | ||||
| -rw-r--r-- | src/store/index.ts (renamed from src/store/index.js) | 12 | ||||
| -rw-r--r-- | src/views/About.vue | 9 | ||||
| -rw-r--r-- | src/views/Editor.vue | 105 | ||||
| -rw-r--r-- | src/vuex-shim.d.ts | 17 |
13 files changed, 191 insertions, 161 deletions
diff --git a/src/App.vue b/src/App.vue index c3a04c9..34945a9 100644 --- a/src/App.vue +++ b/src/App.vue @@ -15,25 +15,4 @@ html { height: 100vh; max-height: 100vh; } - -/* #app { - font-family: Avenir, Helvetica, Arial, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-align: center; - color: #2c3e50; -} - -#nav { - padding: 30px; -} - -#nav a { - font-weight: bold; - color: #2c3e50; -} - -#nav a.router-link-exact-active { - color: #42b983; -} */ </style> diff --git a/src/components/EditorLists.vue b/src/components/EditorLists.vue new file mode 100644 index 0000000..ae0aa22 --- /dev/null +++ b/src/components/EditorLists.vue @@ -0,0 +1,96 @@ +<template> + <div id="editor"> + <div class="column"> + <list :items="peerTeachers" @selection-changed="handlePtClick" #default="pt"> + {{ pt.item.name }} <button @click.stop="deletePeerTeacher(pt.item.id)">X</button> + </list> + </div> + <div class="column"> + <list :items="compatibleLabs" #default="lab"> + {{ lab.item.fullInfo }} <button @click.stop="assignLab(lab.item.id)">+</button> + </list> + </div> + <div class="column"> + <list :items="selectedPeerTeacherAssignments" #default="lab"> + {{ lab.item.fullInfo }} <button @click.stop="unassignLab(lab.item.id)">X</button> + </list> + </div> + </div> +</template> + +<script lang="ts"> +import { defineComponent, PropType } from 'vue'; +import List from '@/components/List.vue'; +import PeerTeacher from '@/models/PeerTeacher'; +import Lab from '@/models/Lab'; + +export default defineComponent({ + name: 'EditorLists', + components: { + List, + }, + props: { + peerTeachers: { + type: Array as PropType<PeerTeacher[]>, + default: [], + }, + labs: { + type: Array as PropType<Lab[]>, + default: [], + }, + }, + computed: { + compatibleLabs(): Lab[] { + const temp = this.labs.filter((lab) => (!this.selectedPeerTeacher.assignedLabs.has(lab.id) + && !this.selectedPeerTeacher.conflictsWith(lab.event))); + + const currentAssignments = this.selectedPeerTeacherAssignments; + return temp.filter((lab) => !currentAssignments + .some((assignedLab) => lab.event.conflictsWith(assignedLab.event))); + }, + selectedPeerTeacherAssignments(): Lab[] { + return Array.from(this.selectedPeerTeacher.assignedLabs.values()) + .flatMap((id) => { + const lab = this.$store.state.labs.get(id); + return (lab === undefined) ? [] : [lab]; + }).sort((a, b) => a.course - b.course || a.section - b.section); + }, + }, + data() { + return { + selectedPeerTeacher: new PeerTeacher(), + }; + }, + methods: { + handlePtClick(peerTeacher: PeerTeacher) { + this.selectedPeerTeacher = peerTeacher; + }, + deletePeerTeacher(id: number) { + if (this.selectedPeerTeacher.id === id) { + this.selectedPeerTeacher = new PeerTeacher(); + } + this.$store.commit('deletePeerTeacher', id); + }, + assignLab(id: string) { + if (this.selectedPeerTeacher.id !== 0) { + this.selectedPeerTeacher.assignedLabs.add(id); + } + }, + unassignLab(id: string) { + this.selectedPeerTeacher.assignedLabs.delete(id); + }, + }, +}); +</script> + +<style> +#editor { + display: flex; + max-height: inherit; +} + +.column { + flex: 1; + overflow: auto; +} +</style> diff --git a/src/features/parser.js b/src/features/parser.ts index f91d926..7839564 100644 --- a/src/features/parser.js +++ b/src/features/parser.ts @@ -2,7 +2,7 @@ import Lab from '@/models/Lab'; import PeerTeacher from '@/models/PeerTeacher'; import EventInfo from '../models/EventInfo'; -export function parseLabFile(file) { +export function parseLabFile(file: File): Promise<Lab[]> { const validCourses = [ '110', '111', @@ -16,26 +16,24 @@ export function parseLabFile(file) { '315', ]; - const reader = new FileReader(); - return new Promise((resolve, reject) => { - const result = []; + const result: Lab[] = []; - reader.onload = (event) => { + file.text().then((text) => { let jsonData; try { - jsonData = JSON.parse(event.target.result); + jsonData = JSON.parse(text); } catch (e) { reject(new Error(e)); } const labs = jsonData.data; - labs.forEach((lab) => { + labs.forEach((lab: any) => { if (validCourses.includes(lab.courseNumber)) { const newLab = new Lab(lab.courseNumber, lab.sequenceNumber); if (lab.instructionalMethod !== 'Web Based') { - lab.meetingsFaculty.every((meeting) => { + lab.meetingsFaculty.every((meeting: any) => { const { meetingTime } = meeting; if (meetingTime.meetingType === 'LAB') { @@ -62,21 +60,17 @@ export function parseLabFile(file) { }); resolve(result); - }; - - reader.readAsText(file); + }); }); } -export function parsePtSchedule(file) { - const reader = new FileReader(); - +export function parsePtSchedule(file: File): Promise<PeerTeacher> { return new Promise((resolve) => { - reader.onload = (event) => { + file.text().then((text) => { const peerTeacher = new PeerTeacher(); const namePattern = /^(.*)\s(.*)\s(\d{9})/; const eventPattern = /^(M?T?W?R?F?)\s(\d{1,2}:\d{2})\s?-\s?(\d{1,2}:\d{2})/; - const lines = event.target.result.split('\n').filter((line) => line.trim()); + const lines = text.split('\n').filter((line) => line.trim()); lines.forEach((line) => { const ptName = line.match(namePattern); @@ -95,18 +89,17 @@ export function parsePtSchedule(file) { }); resolve(peerTeacher); - }; - - reader.readAsText(file); + }); }); } -export function parsePtDatabase(file) { - const reader = new FileReader(); - +export function parsePtDatabase(file: File): Promise<{ + labs: Map<string, Lab>, + peerTeachers: Map<number, PeerTeacher> +}> { return new Promise((resolve) => { - reader.onload = (event) => { - const jsonObj = JSON.parse(event.target.result); + file.text().then((text) => { + const jsonObj = JSON.parse(text); const result = { labs: new Map(), peerTeachers: new Map(), @@ -121,18 +114,16 @@ export function parsePtDatabase(file) { Object.keys(jsonObj.peerTeachers).forEach((key) => { const pt = jsonObj.peerTeachers[key]; const ptObj = new PeerTeacher(pt.firstname, pt.lastname, pt.uin); - ptObj.events = pt.events.map((eventObj) => new EventInfo(eventObj.days, + ptObj.events = pt.events.map((eventObj: any) => new EventInfo(eventObj.days, eventObj.start, eventObj.end)); ptObj.assignedLabs = new Set(); - pt.assignedLabs.forEach((labId) => { + pt.assignedLabs.forEach((labId: string) => { ptObj.assignedLabs.add(labId); }); result.peerTeachers.set(key, ptObj); }); resolve(result); - }; - - reader.readAsText(file); + }); }); } diff --git a/src/main.js b/src/main.ts index c673f53..c673f53 100644 --- a/src/main.js +++ b/src/main.ts diff --git a/src/models/EventInfo.js b/src/models/EventInfo.ts index c1624a8..4493162 100644 --- a/src/models/EventInfo.js +++ b/src/models/EventInfo.ts @@ -1,13 +1,19 @@ export default class EventInfo { - constructor(days = '', start = 0, end = 0) { + days: string; + + start: number; + + end: number; + + constructor(days: string = '', start = 0, end = 0) { this.days = days; this.start = start; this.end = end; } - static timeToStr(time) { + static timeToStr(time: number) { let hour = Math.floor(time / 100); - let minute = time % 100; + const minute = time % 100; const meridiem = (hour < 12) ? 'AM' : 'PM'; if (hour === 0) { @@ -17,13 +23,12 @@ export default class EventInfo { } if (minute < 10) { - minute = `0${minute}`; + return `${hour}:0${minute} ${meridiem}`; } - return `${hour}:${minute} ${meridiem}`; } - conflictsWith(event) { + conflictsWith(event: EventInfo) { const daysConflict = event.days.match(new RegExp(`[${this.days}]`)); if (daysConflict) { diff --git a/src/models/Lab.js b/src/models/Lab.ts index 2e4412d..a6972ef 100644 --- a/src/models/Lab.js +++ b/src/models/Lab.ts @@ -1,6 +1,12 @@ import EventInfo from '@/models/EventInfo'; export default class Lab { + course: number; + + section: number; + + event: EventInfo; + constructor(course = 0, section = 0, event = new EventInfo()) { this.course = course; this.section = section; diff --git a/src/models/PeerTeacher.js b/src/models/PeerTeacher.ts index 00a2f0d..f387431 100644 --- a/src/models/PeerTeacher.js +++ b/src/models/PeerTeacher.ts @@ -1,4 +1,16 @@ +import EventInfo from './EventInfo'; + export default class PeerTeacher { + firstname: string; + + lastname: string; + + uin: number; + + events: EventInfo[]; + + assignedLabs: Set<string>; + constructor(firstname = '', lastname = '', uin = 0) { this.firstname = firstname; this.lastname = lastname; @@ -7,7 +19,7 @@ export default class PeerTeacher { this.assignedLabs = new Set(); } - conflictsWith(event) { + conflictsWith(event: EventInfo) { let conflicts = false; this.events.every((item) => { if (item.conflictsWith(event)) { diff --git a/src/router/index.js b/src/router/index.ts index 7a598b3..7a598b3 100644 --- a/src/router/index.js +++ b/src/router/index.ts diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts new file mode 100644 index 0000000..3804a43 --- /dev/null +++ b/src/shims-vue.d.ts @@ -0,0 +1,6 @@ +/* eslint-disable */ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/src/store/index.js b/src/store/index.ts index 0c3a5c9..e63e257 100644 --- a/src/store/index.js +++ b/src/store/index.ts @@ -1,3 +1,5 @@ +import Lab from '@/models/Lab'; +import PeerTeacher from '@/models/PeerTeacher'; import { createStore } from 'vuex'; export default createStore({ @@ -6,24 +8,24 @@ export default createStore({ peerTeachers: new Map(), }, mutations: { - setLabs(state, labs) { + setLabs(state, labs: Map<string, Lab>) { state.labs = labs; }, - setPeerTeachers(state, peerTeachers) { + setPeerTeachers(state, peerTeachers: Map<number, PeerTeacher>) { state.peerTeachers = peerTeachers; }, - importLabs(state, labs) { + importLabs(state, labs: Lab[]) { state.labs.clear(); labs.forEach((lab) => { state.labs.set(lab.id, lab); }); }, - addPeerTeachers(state, peerTeachers) { + addPeerTeachers(state, peerTeachers: PeerTeacher[]) { peerTeachers.forEach((pt) => { state.peerTeachers.set(pt.id, pt); }); }, - deletePeerTeacher(state, id) { + deletePeerTeacher(state, id: number) { state.peerTeachers.delete(id); }, }, diff --git a/src/views/About.vue b/src/views/About.vue index ab4725e..ac2bde1 100644 --- a/src/views/About.vue +++ b/src/views/About.vue @@ -7,18 +7,19 @@ </div> </template> -<script> +<script lang="ts"> +import { defineComponent } from 'vue'; import FileUpload from '../components/FileUpload.vue'; import { parsePtDatabase } from '../features/parser'; -export default { +export default defineComponent({ name: 'About', components: { FileUpload, }, methods: { - handleDatabaseChange(files) { + handleDatabaseChange(files: File[]) { parsePtDatabase(files[0]).then((result) => { this.$store.commit('setLabs', result.labs); this.$store.commit('setPeerTeachers', result.peerTeachers); @@ -26,5 +27,5 @@ export default { }); }, }, -}; +}); </script> diff --git a/src/views/Editor.vue b/src/views/Editor.vue index 5ef0131..b4a2e8e 100644 --- a/src/views/Editor.vue +++ b/src/views/Editor.vue @@ -1,123 +1,38 @@ <template> <div id="home"> <action-bar /> - <div id="editor"> - <div class="column"> - <list :items="peerTeachers" @selection-changed="handlePtClick" #default="pt"> - {{ pt.item.name }} <button @click.stop="deletePeerTeacher(pt.item.id)">X</button> - </list> - </div> - <div class="column"> - <list :items="compatibleLabs" #default="lab"> - {{ lab.item.fullInfo }} <button @click.stop="assignLab(lab.item.id)">+</button> - </list> - </div> - <div class="column"> - <list :items="selectedPeerTeacherAssignments" #default="lab"> - {{ lab.item.fullInfo }} <button @click.stop="unassignLab(lab.item.id)">X</button> - </list> - </div> - </div> + <editor-lists :peerTeachers="peerTeachers" :labs="labs" /> </div> </template> -<script> +<script lang="ts"> +import { defineComponent } from 'vue'; import ActionBar from '@/components/ActionBar.vue'; -import List from '@/components/List.vue'; +import EditorLists from '@/components/EditorLists.vue'; import PeerTeacher from '@/models/PeerTeacher'; +import Lab from '@/models/Lab'; -export default { +export default defineComponent({ name: 'Editor', components: { ActionBar, - List, + EditorLists, }, computed: { - labs() { + labs(): Lab[] { return Array.from(this.$store.state.labs.values()).sort((a, b) => a.id .localeCompare(b.id)); }, - peerTeachers() { + peerTeachers(): PeerTeacher[] { return Array.from(this.$store.state.peerTeachers.values()).sort((a, b) => a.lastname .toUpperCase().localeCompare(b.lastname.toUpperCase())); }, - compatibleLabs() { - const temp = this.labs.filter((lab) => (!this.selectedPeerTeacher.assignedLabs.has(lab.id) - && !this.selectedPeerTeacher.conflictsWith(lab.event))); - - const currentAssignments = this.selectedPeerTeacherAssignments; - return temp.filter((lab) => { - let compatible = true; - currentAssignments.every((assignedLab) => { - if (lab.event.conflictsWith(assignedLab.event)) { - compatible = false; - return false; - } - return true; - }); - return compatible; - }); - }, - selectedPeerTeacherAssignments() { - return [...this.selectedPeerTeacher.assignedLabs.values()].map((id) => this.$store.state.labs - .get(id)).sort((a, b) => { - if (a.course < b.course) { - return -1; - } - if (b.course < a.course) { - return 1; - } - - if (a.section < b.section) { - return -1; - } - if (b.section < a.section) { - return 1; - } - - return 0; - }); - }, - }, - data() { - return { - selectedPeerTeacher: new PeerTeacher(), - }; - }, - methods: { - handlePtClick(peerTeacher) { - this.selectedPeerTeacher = peerTeacher; - }, - deletePeerTeacher(id) { - if (this.selectedPeerTeacher.id === id) { - this.selectedPeerTeacher = new PeerTeacher(); - } - this.$store.commit('deletePeerTeacher', id); - }, - assignLab(id) { - if (this.selectedPeerTeacher.id !== 0) { - this.selectedPeerTeacher.assignedLabs.add(id); - } - }, - unassignLab(id) { - this.selectedPeerTeacher.assignedLabs.delete(id); - }, }, -}; +}); </script> <style> #home { max-height: inherit; } - -#editor { - display: flex; - max-height: inherit; -} - -.column { - flex: 1; - overflow: auto; -} </style> diff --git a/src/vuex-shim.d.ts b/src/vuex-shim.d.ts new file mode 100644 index 0000000..c8a55f9 --- /dev/null +++ b/src/vuex-shim.d.ts @@ -0,0 +1,17 @@ +import { Store } from 'vuex'; + +import PeerTeacher from './models/PeerTeacher'; +import Lab from './models/Lab'; + +declare module '@vue/runtime-core' { + // declare your own store states + interface State { + peerTeachers: Map<number, PeerTeacher>, + labs: Map<string, Lab> + } + + // provide typings for `this.$store` + interface ComponentCustomProperties { + $store: Store<State> + } +} |
