Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 970a058ba3 | |||
| 5d295db1b7 | |||
| b22aac7ca2 | |||
| eef29a4611 | |||
| 131952ed37 | |||
| 2e95c2550e | |||
| 0db6e0d693 | |||
| 27198ad4e0 | |||
| 3cbb6ebee6 | |||
| 2308df65b5 | |||
| c8f82cc991 | |||
| a6e1b905ed | |||
| 9432e9a806 | |||
| d90280a10f | |||
| db30b7f807 | |||
| 20910b1562 | |||
| 5659a049e5 | |||
| 085812d859 | |||
| e60a4669ac | |||
| ac84907a98 | |||
| 04ee69e8e2 | |||
| a2445aca6a | |||
| 336a58b23d | |||
| 5a5cf9bd4d | |||
| 44d262f457 | |||
| 27fda43373 | |||
| b3de3b8405 | |||
| 633918aa40 | |||
| 3bc59e87d3 | |||
| af7d9e9dca | |||
| 3b8884dc84 | |||
| 92158684c0 | |||
| b37de9cf24 | |||
| cd2dff5559 | |||
| d6a85af15c | |||
| 0d333879ca | |||
| 905db3c73a | |||
| c5dfad8be8 | |||
| c9454a3832 | |||
| c732d475f1 | |||
| a4b7e0fd46 | |||
| bcee069b32 | |||
| 182208f8c3 |
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>404: Redirecting...</title>
|
||||
<meta http-equiv = "refresh" content = "0; url = https://vera.hydev.org/" />
|
||||
</head>
|
||||
<body>
|
||||
404 Not Found! Redirecting to (<a href="https://vera.hydev.org">https://vera.hydev.org</a>)...
|
||||
<script>
|
||||
window.location.href = 'https://vera.hydev.org';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,6 +5,7 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
#app-content
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Component, Vue} from 'vue-property-decorator';
|
||||
import Login from '@/components/login/login';
|
||||
import Navigation from '@/components/navigation/navigation';
|
||||
import Overall from '@/pages/overall/overall';
|
||||
import Overall from '@/pages/overall/overall.vue';
|
||||
import Constants from '@/constants';
|
||||
import JsonUtils from '@/utils/json-utils';
|
||||
import pWaitFor from 'p-wait-for';
|
||||
@@ -9,7 +9,8 @@ import {HttpUtils} from '@/utils/http-utils';
|
||||
import {CourseUtils} from '@/utils/course-utils';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
import Loading from '@/components/loading/loading.vue';
|
||||
import CoursePage from '@/pages/course/course-page';
|
||||
import CoursePage from '@/pages/course/course-page.vue';
|
||||
import {FormatUtils} from '@/utils/format-utils';
|
||||
|
||||
|
||||
/**
|
||||
@@ -146,6 +147,9 @@ export default class App extends Vue
|
||||
// Save courses
|
||||
this.courses = response.data;
|
||||
|
||||
// Post processing
|
||||
CourseUtils.postProcess(this.courses);
|
||||
|
||||
// Load assignments
|
||||
this.loadAssignments();
|
||||
}
|
||||
|
||||
@@ -34,11 +34,9 @@
|
||||
})
|
||||
export default class Loading extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop() text: string;
|
||||
@Prop({required: true}) text: string;
|
||||
|
||||
// @ts-ignore
|
||||
@Prop() error: boolean;
|
||||
@Prop({required: true}) error: boolean;
|
||||
|
||||
getText()
|
||||
{
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Component, Vue} from 'vue-property-decorator';
|
||||
import Constants from '@/constants';
|
||||
import {HttpUtils} from '@/utils/http-utils';
|
||||
import App from '@/components/app/app';
|
||||
import VersionUtils from '@/utils/version-utils';
|
||||
|
||||
/**
|
||||
* This component handles user login, and obtains data from the server.
|
||||
* TODO: Press enter to login
|
||||
*/
|
||||
@Component({
|
||||
components: {},
|
||||
@@ -24,21 +22,26 @@ export default class Login extends Vue
|
||||
*/
|
||||
public created()
|
||||
{
|
||||
// Check cookies version
|
||||
if (this.needToUpdateCookies())
|
||||
{
|
||||
console.log('Version Updated! Clearing cookies...');
|
||||
|
||||
// Clear all cookies
|
||||
this.$cookies.keys().forEach(key => this.$cookies.remove(key));
|
||||
}
|
||||
|
||||
// Check login cookies
|
||||
if (this.$cookies.isKey('va.token'))
|
||||
{
|
||||
// Check cookies version
|
||||
if (this.needToUpdateCookies())
|
||||
{
|
||||
console.log('Version Updated! Clearing cookies...');
|
||||
|
||||
// Clear all cookies
|
||||
this.$cookies.keys().forEach(key => this.$cookies.remove(key));
|
||||
}
|
||||
|
||||
// Already contains valid token / TODO: Validate
|
||||
// TODO: Update token each access
|
||||
this.$emit('login:token', this.$cookies.get('va.token'));
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Cookies doesn\'t exist');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +75,7 @@ export default class Login extends Vue
|
||||
{
|
||||
// Save token to cookies
|
||||
this.$cookies.set('va.token', response.data, '27d');
|
||||
this.$cookies.set('va.version', Constants.VERSION);
|
||||
this.$cookies.set('va.version', Constants.VERSION, '27d');
|
||||
|
||||
// Call custom event with token
|
||||
this.$emit('login:token', response.data);
|
||||
|
||||
@@ -5,23 +5,24 @@
|
||||
<img alt="Vue logo" src="../../assets/logo.png">
|
||||
|
||||
<h1>Veracross Analyzer</h1>
|
||||
<form id="login-form">
|
||||
<el-input v-model="username"
|
||||
placeholder="School Username"
|
||||
:class="{'input-error': error !== ''}"
|
||||
@keyup.enter.native="onEnter">
|
||||
</el-input>
|
||||
|
||||
<el-input v-model="username"
|
||||
placeholder="School Username"
|
||||
:class="{'input-error': error !== ''}"
|
||||
@keyup.enter.native="onEnter">
|
||||
</el-input>
|
||||
<el-input v-model="password"
|
||||
placeholder="Veracross Password"
|
||||
show-password=""
|
||||
:class="{'input-error': error !== ''}"
|
||||
@keyup.enter.native="onEnter">
|
||||
</el-input>
|
||||
|
||||
<el-input v-model="password"
|
||||
placeholder="Veracross Password"
|
||||
show-password=""
|
||||
:class="{'input-error': error !== ''}"
|
||||
@keyup.enter.native="onEnter">
|
||||
</el-input>
|
||||
<div class="el-form-item__error custom">{{error}}</div>
|
||||
|
||||
<div class="el-form-item__error custom">{{error}}</div>
|
||||
|
||||
<el-button plain type="primary" @click="onLoginClick" :loading="loading">Login</el-button>
|
||||
<el-button plain type="primary" @click="onLoginClick" :loading="loading">Login</el-button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -57,9 +57,6 @@
|
||||
left: 25%;
|
||||
padding-top: 2px;
|
||||
box-shadow: 0 -2px 9px 0 #ecebeb;
|
||||
|
||||
// Trapezoid
|
||||
transform: perspective(50px) rotateX(10deg);
|
||||
}
|
||||
|
||||
#prev-course
|
||||
@@ -71,9 +68,6 @@
|
||||
left: 25%;
|
||||
padding-bottom: 2px;
|
||||
box-shadow: 0 2px 9px 0 #ecebeb;
|
||||
|
||||
// Trapezoid
|
||||
transform: perspective(50px) rotateX(-10deg);
|
||||
}
|
||||
|
||||
.nav-course-operations
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import Constants from '@/constants';
|
||||
import {Course} from '@/components/app/app';
|
||||
import App, {Course} from '@/components/app/app';
|
||||
import {CourseUtils} from '@/utils/course-utils';
|
||||
import {FormatUtils} from '@/utils/format-utils';
|
||||
import pWaitFor from 'p-wait-for';
|
||||
|
||||
/**
|
||||
* This component is the top navigation bar
|
||||
@@ -12,11 +12,9 @@ import {FormatUtils} from '@/utils/format-utils';
|
||||
})
|
||||
export default class Navigation extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop() activeIndex: string;
|
||||
@Prop({required: true}) activeIndex: string;
|
||||
|
||||
// @ts-ignore
|
||||
@Prop() courses: Course[];
|
||||
@Prop({required: true}) courses: Course[];
|
||||
|
||||
// Instance
|
||||
public static instance: Navigation;
|
||||
@@ -24,18 +22,21 @@ export default class Navigation extends Vue
|
||||
/**
|
||||
* This is called when the instance is created.
|
||||
*/
|
||||
public created()
|
||||
public mounted()
|
||||
{
|
||||
// Set instance
|
||||
Navigation.instance = this;
|
||||
|
||||
// Set history state
|
||||
let url = window.location.pathname;
|
||||
if (url == '/' || url == '') url = '/overall';
|
||||
let url = '/' + window.location.hash;
|
||||
if (url == '/' || url == '') url = '/#overall';
|
||||
window.history.replaceState({lastTab: url.substring(1)}, '', url);
|
||||
|
||||
// Update initial index
|
||||
this.updateIndex(url.substring(1), false);
|
||||
// Update initial index after loading is done
|
||||
pWaitFor(() => this.courses.length > 1 && App.instance.loading != '').then(() =>
|
||||
{
|
||||
this.updateIndex(url.substring(2), false);
|
||||
});
|
||||
|
||||
// Create history state listener
|
||||
window.onpopstate = e =>
|
||||
@@ -80,11 +81,8 @@ export default class Navigation extends Vue
|
||||
// Record or not
|
||||
if (history == null || history)
|
||||
{
|
||||
// Debug output TODO: Remove this
|
||||
console.log(`onNavigate: Previous: ${this.activeIndex}, New: ${newIndex}`);
|
||||
|
||||
// Check url
|
||||
let url = `/${newIndex}`;
|
||||
let url = `/#${newIndex}`;
|
||||
|
||||
// Push history state
|
||||
window.history.pushState({lastTab: newIndex}, '', url);
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ export default class Constants
|
||||
public static API_URL: string = 'https://va.hydev.org/api';
|
||||
|
||||
/** Current version */
|
||||
public static VERSION: string = '0.3.6.741';
|
||||
public static VERSION: string = '0.3.6.784';
|
||||
|
||||
/** Minimum version that still supports the same cookies */
|
||||
public static MIN_SUPPORTED_VERSION: string = '0.3.4.561';
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
import Vue from 'vue';
|
||||
import ElementUI from 'element-ui';
|
||||
const VCharts = require('v-charts');
|
||||
|
||||
import App from './components/app/app.vue';
|
||||
import VueCookies from 'vue-cookies';
|
||||
|
||||
const VCharts = require('v-charts');
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
// Use Element UI
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment, Course} from '@/components/app/app';
|
||||
import CourseHead from '@/pages/overall/overall-course/course-head/course-head.vue';
|
||||
import CourseScatter from '@/pages/course/course-scatter/course-scatter';
|
||||
|
||||
@Component({
|
||||
components: {CourseHead, CourseScatter}
|
||||
})
|
||||
export default class CoursePage extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) course: Course;
|
||||
|
||||
private unread: number = -1;
|
||||
private unreadAssignments: Assignment[] = [];
|
||||
|
||||
/**
|
||||
* Count the number of unread assignments with cache
|
||||
*/
|
||||
countUnread(): number
|
||||
{
|
||||
if (this.unread == -1)
|
||||
{
|
||||
this.unreadAssignments = this.course.assignments.filter(a => a.unread);
|
||||
return this.unread = this.unreadAssignments.length;
|
||||
}
|
||||
else return this.unread;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-card id="course-card" class="course-card">
|
||||
<course-head :course="course" :unread="countUnread()"></course-head>
|
||||
<course-head :clickable="false" :course="course" :unread="countUnread()"></course-head>
|
||||
|
||||
<div class="course-card-content expand">
|
||||
<el-row>
|
||||
@@ -13,9 +13,44 @@
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!--AssignmentEntry v-for="assignment in course.assignments"
|
||||
:assignment="assignment" :unread="false">
|
||||
</AssignmentEntry-->
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script src="./course-page.ts" lang="ts"></script>
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment, Course} from '@/components/app/app';
|
||||
import CourseHead from '@/pages/overall/overall-course/course-head/course-head.vue';
|
||||
import CourseScatter from '@/pages/course/course-scatter/course-scatter';
|
||||
import AssignmentEntry from '@/pages/overall/overall-course/assignment-entry/assignment-entry.vue';
|
||||
|
||||
@Component({
|
||||
components: {AssignmentEntry, CourseHead, CourseScatter}
|
||||
})
|
||||
export default class CoursePage extends Vue
|
||||
{
|
||||
@Prop({required: true}) course: Course;
|
||||
|
||||
private unread: number = -1;
|
||||
private unreadAssignments: Assignment[] = [];
|
||||
|
||||
/**
|
||||
* Count the number of unread assignments with cache
|
||||
*/
|
||||
countUnread(): number
|
||||
{
|
||||
if (this.unread == -1)
|
||||
{
|
||||
this.unreadAssignments = this.course.assignments.filter(a => a.unread);
|
||||
return this.unread = this.unreadAssignments.length;
|
||||
}
|
||||
else return this.unread;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="./course-page.scss" lang="scss" scoped></style>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment, Course} from '@/components/app/app';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
import Constants from '@/constants';
|
||||
import {FormatUtils} from '@/utils/format-utils';
|
||||
import moment from 'moment';
|
||||
@@ -11,7 +10,6 @@ export default class CourseScatter extends Vue
|
||||
{
|
||||
private static DOT = '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:{color}"></span>';
|
||||
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) course: Course;
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,6 @@ import {FormatUtils} from '@/utils/format-utils';
|
||||
})
|
||||
export default class OverallBar extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) courses: Course[];
|
||||
|
||||
/**
|
||||
|
||||
+37
-3
@@ -5,6 +5,7 @@
|
||||
<span class="month">{{getMoment(assignment.date).format("MMM Do")}}</span>
|
||||
<span class="now">({{getMoment(assignment.date).fromNow()}})</span>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="15" class="description">
|
||||
<span class="type entry-box"
|
||||
:style="`border-color: var(--assignment-type-${assignment.typeId})`">
|
||||
@@ -12,6 +13,7 @@
|
||||
</span>
|
||||
<span class="text">{{assignment.description}}</span>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6" class="grade">
|
||||
<span class="percent entry-box">
|
||||
{{(assignment.score / assignment.scoreMax * 100).toFixed(1)}}
|
||||
@@ -19,13 +21,45 @@
|
||||
</span>
|
||||
<span class="score entry-box">{{assignment.score}}</span>
|
||||
<span class="max entry-box">{{assignment.scoreMax}}</span>
|
||||
|
||||
<el-button class="mark-as-read" size="mini" type="text"
|
||||
icon="el-icon-close" @click="markAsRead">
|
||||
icon="el-icon-close" @click="markAsRead" v-if="unread">
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./unread-entry.ts" lang="ts"></script>
|
||||
<style src="./unread-entry.scss" lang="scss" scoped></style>
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment} from '@/components/app/app';
|
||||
import moment from 'moment';
|
||||
|
||||
@Component
|
||||
export default class AssignmentEntry extends Vue
|
||||
{
|
||||
@Prop({required: true}) assignment: Assignment;
|
||||
|
||||
@Prop({default: false}) unread: boolean;
|
||||
|
||||
/**
|
||||
* Format a date to the displayed format
|
||||
*
|
||||
* @param date Date
|
||||
*/
|
||||
getMoment(date: string)
|
||||
{
|
||||
return moment(new Date(date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this unread assignment as read
|
||||
*/
|
||||
markAsRead()
|
||||
{
|
||||
// Call custom event
|
||||
this.$emit('mark-as-read', this.assignment)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style src="./assignment-entry.scss" lang="scss" scoped></style>
|
||||
@@ -0,0 +1,92 @@
|
||||
// Main card content
|
||||
.course-card-content.main
|
||||
{
|
||||
padding: 0 20px 0 20px;
|
||||
height: 90px;
|
||||
|
||||
// Main color
|
||||
background: white;
|
||||
}
|
||||
|
||||
.course-col-name
|
||||
{
|
||||
// Align left
|
||||
text-align: left;
|
||||
float: left;
|
||||
|
||||
.course-name
|
||||
{
|
||||
overflow: hidden;
|
||||
font-size: 22px;
|
||||
color: var(--main);
|
||||
}
|
||||
|
||||
.course-teacher
|
||||
{
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.course-col-grade
|
||||
{
|
||||
// Align right
|
||||
text-align: right;
|
||||
float: right;
|
||||
|
||||
// Adjust position
|
||||
margin-top: -2px;
|
||||
|
||||
.course-grade
|
||||
{
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.course-updates
|
||||
{
|
||||
font-size: 14px;
|
||||
|
||||
.unread-number
|
||||
{
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.unread-text
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.course-updates.unread
|
||||
{
|
||||
.unread-number
|
||||
{
|
||||
background: var(--unread);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.unread-text
|
||||
{
|
||||
color: var(--unread);
|
||||
}
|
||||
}
|
||||
|
||||
.course-updates.none
|
||||
{
|
||||
color: #999999;
|
||||
|
||||
.unread-number
|
||||
{
|
||||
background: #eeeeee;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,23 +25,18 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import App, {Course} from '@/components/app/app';
|
||||
import {Course} from '@/components/app/app';
|
||||
import {CourseUtils} from '@/utils/course-utils';
|
||||
import Navigation from '@/components/navigation/navigation';
|
||||
|
||||
@Component({
|
||||
components: {}
|
||||
})
|
||||
@Component
|
||||
export default class CourseHead extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop() unread: number;
|
||||
@Prop({required: true}) unread: number;
|
||||
|
||||
// @ts-ignore
|
||||
@Prop() course: Course;
|
||||
@Prop({required: true}) course: Course;
|
||||
|
||||
// @ts-ignore
|
||||
@Prop() clickable: boolean;
|
||||
@Prop({required: true}) clickable: boolean;
|
||||
|
||||
/**
|
||||
* Redirect to the course page
|
||||
@@ -54,97 +49,4 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// Main card content
|
||||
.course-card-content.main
|
||||
{
|
||||
padding: 0 20px 0 20px;
|
||||
height: 90px;
|
||||
|
||||
// Main color
|
||||
background: white;
|
||||
}
|
||||
|
||||
.course-col-name
|
||||
{
|
||||
// Align left
|
||||
text-align: left;
|
||||
float: left;
|
||||
|
||||
.course-name
|
||||
{
|
||||
overflow: hidden;
|
||||
font-size: 22px;
|
||||
color: var(--main);
|
||||
}
|
||||
|
||||
.course-teacher
|
||||
{
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.course-col-grade
|
||||
{
|
||||
// Align right
|
||||
text-align: right;
|
||||
float: right;
|
||||
|
||||
// Adjust position
|
||||
margin-top: -2px;
|
||||
|
||||
.course-grade
|
||||
{
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.course-updates
|
||||
{
|
||||
font-size: 14px;
|
||||
|
||||
.unread-number
|
||||
{
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.unread-text
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.course-updates.unread
|
||||
{
|
||||
.unread-number
|
||||
{
|
||||
background: var(--unread);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.unread-text
|
||||
{
|
||||
color: var(--unread);
|
||||
}
|
||||
}
|
||||
|
||||
.course-updates.none
|
||||
{
|
||||
color: #999999;
|
||||
|
||||
.unread-number
|
||||
{
|
||||
background: #eeeeee;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style src="./course-head.scss" lang="scss"></style>
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import App, {Assignment, Course} from '@/components/app/app';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
import Constants from '@/constants';
|
||||
import UnreadEntry from '@/pages/overall/overall-course/unread-entry/unread-entry';
|
||||
import AssignmentEntry from '@/pages/overall/overall-course/assignment-entry/assignment-entry.vue';
|
||||
import CourseHead from '@/pages/overall/overall-course/course-head/course-head.vue';
|
||||
|
||||
@Component({
|
||||
components: {UnreadEntry, CourseHead}
|
||||
components: {UnreadEntry: AssignmentEntry, CourseHead}
|
||||
})
|
||||
export default class OverallCourse extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) course: Course;
|
||||
|
||||
private unread: number = -1;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<unread-entry v-for="assignment in unreadAssignments"
|
||||
:assignment="assignment"
|
||||
:key="assignment.id"
|
||||
unread="true"
|
||||
v-on:mark-as-read="markAsRead">
|
||||
</unread-entry>
|
||||
</div>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment, Course} from '@/components/app/app';
|
||||
import moment from 'moment';
|
||||
|
||||
@Component({
|
||||
})
|
||||
export default class UnreadEntry extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) assignment: Assignment;
|
||||
|
||||
/**
|
||||
* Format a date to the displayed format
|
||||
*
|
||||
* @param date Date
|
||||
*/
|
||||
getMoment(date: string)
|
||||
{
|
||||
return moment(new Date(date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this unread assignment as read
|
||||
*/
|
||||
markAsRead()
|
||||
{
|
||||
// Call custom event
|
||||
this.$emit('mark-as-read', this.assignment)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Course} from '@/components/app/app';
|
||||
import moment from 'moment';
|
||||
|
||||
@Component({
|
||||
})
|
||||
export default class OverallLine extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) courses: Course[];
|
||||
|
||||
private settings =
|
||||
@@ -38,9 +38,8 @@ export default class OverallLine extends Vue
|
||||
// Zoom bar
|
||||
dataZoom:
|
||||
[
|
||||
// TODO: Calculate real value for startValue
|
||||
{
|
||||
startValue: '9/13/2019'
|
||||
startValue: moment().subtract(30, 'days').format('M/D/YYYY')
|
||||
},
|
||||
{
|
||||
type: 'inside'
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import OverallLine from '@/pages/overall/overall-line/overall-line';
|
||||
import OverallBar from '@/pages/overall/overall-bar/overall-bar';
|
||||
import OverallCourse from '@/pages/overall/overall-course/overall-course';
|
||||
import {Course} from '@/components/app/app';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
|
||||
@Component({
|
||||
components: {OverallLine, OverallBar, OverallCourse}
|
||||
})
|
||||
export default class Overall extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) courses: Course[];
|
||||
|
||||
/**
|
||||
* This function is called to get gpa since I can't import another
|
||||
* class in the Vue file.
|
||||
*/
|
||||
public getGPA()
|
||||
{
|
||||
return GPAUtils.getGPA(this.courses);
|
||||
}
|
||||
}
|
||||
@@ -32,5 +32,30 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./overall.ts" lang="ts"></script>
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import OverallLine from '@/pages/overall/overall-line/overall-line';
|
||||
import OverallBar from '@/pages/overall/overall-bar/overall-bar';
|
||||
import OverallCourse from '@/pages/overall/overall-course/overall-course';
|
||||
import {Course} from '@/components/app/app';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
|
||||
@Component({
|
||||
components: {OverallLine, OverallBar, OverallCourse}
|
||||
})
|
||||
export default class Overall extends Vue
|
||||
{
|
||||
@Prop({required: true}) courses: Course[];
|
||||
|
||||
/**
|
||||
* This function is called to get gpa since I can't import another
|
||||
* class in the Vue file.
|
||||
*/
|
||||
public getGPA()
|
||||
{
|
||||
return GPAUtils.getGPA(this.courses);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="./overall.scss" lang="scss" scoped></style>
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import Vue, { VNode } from 'vue';
|
||||
import Vue, {VNode} from 'vue';
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
|
||||
Vendored
+4
-3
@@ -1,4 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue';
|
||||
export default Vue;
|
||||
declare module '*.vue'
|
||||
{
|
||||
import Vue from 'vue';
|
||||
export default Vue;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
import {Course} from '@/components/app/app';
|
||||
import {FormatUtils} from '@/utils/format-utils';
|
||||
|
||||
const LEVEL_AP = {level: 'AP', scaleUp: 1};
|
||||
const LEVEL_H = {level: 'H', scaleUp: 0.75};
|
||||
const LEVEL_A = {level: 'A', scaleUp: 0.5};
|
||||
const LEVEL_CP = {level: 'CP', scaleUp: 0.25};
|
||||
const LEVEL_CLUB = {level: 'Club', scaleUp: -1};
|
||||
|
||||
const UNKNOWN_COURSE_LIST = new Map();
|
||||
UNKNOWN_COURSE_LIST.set('Piano Masterclass', LEVEL_H);
|
||||
UNKNOWN_COURSE_LIST.set('Multivariable Calculus with Differential Equations', LEVEL_H);
|
||||
UNKNOWN_COURSE_LIST.set('Introduction to Algorithmic Thinking and Computational Technologies', LEVEL_A);
|
||||
UNKNOWN_COURSE_LIST.set('Ceramics 1', LEVEL_CP);
|
||||
UNKNOWN_COURSE_LIST.set('Ceramics 2', LEVEL_A);
|
||||
UNKNOWN_COURSE_LIST.set('Sculpture', LEVEL_CP);
|
||||
UNKNOWN_COURSE_LIST.set('Drawing', LEVEL_CP);
|
||||
UNKNOWN_COURSE_LIST.set('Painting', LEVEL_CP);
|
||||
|
||||
export class CourseUtils
|
||||
{
|
||||
@@ -19,8 +36,8 @@ export class CourseUtils
|
||||
// Skip future or past courses
|
||||
if (course.status != 'active') return;
|
||||
|
||||
// Skip courses without levels
|
||||
if (course.level == 'None') return;
|
||||
// Skip courses without levels TODO: Ask for user input
|
||||
if (course.level == 'None' || course.level == 'Unknown' || course.scaleUp == -1) return;
|
||||
|
||||
// Skip courses without graded assignments
|
||||
if (course.assignments.filter(a => a.complete == 'Complete').length == 0) return;
|
||||
@@ -45,4 +62,66 @@ export class CourseUtils
|
||||
{
|
||||
return `course/${course.id}/${course.name.toLowerCase().split(' ').join('-')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post process course list
|
||||
*
|
||||
* @param courses Course list
|
||||
*/
|
||||
public static postProcess(courses: Course[])
|
||||
{
|
||||
for (let course of courses)
|
||||
{
|
||||
// Parse name
|
||||
course.name = FormatUtils.parseText(course.name).trim();
|
||||
|
||||
// Detect level
|
||||
let level = this.detectLevel(course.name);
|
||||
if (level != undefined)
|
||||
{
|
||||
course.level = level.level;
|
||||
course.scaleUp = level.scaleUp;
|
||||
}
|
||||
else
|
||||
{
|
||||
course.level = 'Unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect course level based on course name
|
||||
*
|
||||
* @param name Course name
|
||||
*/
|
||||
private static detectLevel(name: string)
|
||||
{
|
||||
// Common ones
|
||||
if (name.startsWith('AP')) return LEVEL_AP;
|
||||
if (name.endsWith(' H')) return LEVEL_H;
|
||||
if (name.endsWith(' A')) return LEVEL_A;
|
||||
if (name.endsWith(' CP')) return LEVEL_CP;
|
||||
if (name.startsWith('HS ')) return LEVEL_CLUB;
|
||||
if (name.startsWith('MS ')) return LEVEL_CLUB;
|
||||
|
||||
// Uncommon ones
|
||||
let lower = name.toLowerCase();
|
||||
|
||||
if (name.startsWith('Pre-AP')) return LEVEL_AP;
|
||||
if (lower.endsWith(' acc')) return LEVEL_A;
|
||||
if (name.endsWith('H')) return LEVEL_H;
|
||||
if (name.endsWith('A')) return LEVEL_A;
|
||||
if (name.endsWith('CP')) return LEVEL_CP;
|
||||
|
||||
// Even more uncommon
|
||||
if (lower.includes('honors')) return LEVEL_H;
|
||||
if (lower.includes('accelerated')) return LEVEL_A;
|
||||
if (name.includes('Advanced')) return LEVEL_A;
|
||||
|
||||
// Unknown course list
|
||||
if (UNKNOWN_COURSE_LIST.has(name)) return UNKNOWN_COURSE_LIST.get(name);
|
||||
|
||||
// Really unknown
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,4 +37,14 @@ export class FormatUtils
|
||||
{
|
||||
return str.replace(/\w\S*/g, s => s.charAt(0).toUpperCase() + s.substr(1).toLowerCase())
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse html text
|
||||
*
|
||||
* @param str
|
||||
*/
|
||||
public static parseText(str: string): string
|
||||
{
|
||||
return str.replace(/&/g, '&');
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -24,7 +24,10 @@
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
],
|
||||
|
||||
// Custom
|
||||
"strictPropertyInitialization": false
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
|
||||
Reference in New Issue
Block a user