Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d02cfe0557 | |||
| aca0b5635b | |||
| 4e8d3aaeb9 | |||
| 1c097b639a | |||
| ed80b3c82b | |||
| 1a59e174ae | |||
| bbca7c2e14 | |||
| 5c09490d5e | |||
| ba9b24a276 | |||
| 755b61efe3 | |||
| 75d6d94757 | |||
| a00285b38a | |||
| b529aa070a | |||
| 38d7b75831 | |||
| c3b81b5828 | |||
| 8aae753098 | |||
| 382a2aeabc | |||
| ec056ef3b3 | |||
| 96d3e6b620 | |||
| 85f59a3983 | |||
| 71035ab87b | |||
| bfd91af873 | |||
| de5d406362 | |||
| 8c8f405aa5 | |||
| a59f2e6f7c | |||
| 5d06ee1c41 | |||
| b818420df7 | |||
| dbe9c69771 | |||
| 3a58699f62 | |||
| f5804c3ce8 | |||
| 5ef7083ee6 | |||
| 25370ec412 | |||
| b55c25aa7c | |||
| 9f68ac8377 | |||
| 335cde4d4e | |||
| 878b8cf87c | |||
| 283904d976 | |||
| 11dea5182b | |||
| 87f8f49c39 | |||
| 595033b80f | |||
| ccd32ed7e6 | |||
| a40e3b24eb | |||
| 6a4ff9b0a7 | |||
| 2dfb0bf447 | |||
| 21e6352ace | |||
| f1089dbdf5 | |||
| e95efbff59 | |||
| 92b46baeed | |||
| d45fce6506 | |||
| e4374a6731 | |||
| cd402c4b86 | |||
| 25bf445582 | |||
| 74287289e0 | |||
| cc23f8565a | |||
| a537ced0d7 | |||
| 797d962e40 | |||
| 992be72d8d | |||
| 375712c0a8 | |||
| 0b7ab5923c | |||
| d077818508 | |||
| c991b75384 | |||
| 9f59f517b5 | |||
| 6654f78486 | |||
| 206fef682f | |||
| 3e454423a1 | |||
| 8326a9e923 | |||
| 5013830815 | |||
| e4ba53f460 | |||
| fabdc10467 | |||
| 91488c6a9f | |||
| f346364633 | |||
| 34e482e8a9 | |||
| 9aaf662a60 | |||
| 0b65ebbb00 | |||
| 74d1c88f82 | |||
| 70c8375810 | |||
| b554128337 | |||
| 4a8ce2ca18 | |||
| a2df22c1da | |||
| 047ceda252 | |||
| c94435360f | |||
| ed0b7a9740 | |||
| b168c4bb82 | |||
| 5078011f40 | |||
| 1428953cb7 | |||
| 7f962704d2 | |||
| 2970263db5 | |||
| 91d9fb90ac | |||
| adb28089b9 | |||
| 6f4a012ac4 | |||
| b807a41496 | |||
| 02f1fb797f | |||
| caee9117b3 | |||
| 48cec4c0f0 | |||
| d4df0c3562 | |||
| 8caab70b25 | |||
| 846f26db75 | |||
| ea2bdb226d | |||
| 0f93a3581f | |||
| 6607afc898 | |||
| 543b0ddefe | |||
| 6d191f04fb | |||
| 353a623e5f | |||
| e28f68aae3 | |||
| 5441592d68 | |||
| a8e96b142d | |||
| 4527b58084 | |||
| 80d14c1400 |
@@ -25,4 +25,14 @@
|
|||||||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||||||
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Q615K1KFLC"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-Q615K1KFLC');
|
||||||
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
#app
|
#app
|
||||||
{
|
{
|
||||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
font-family: var(--font);
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#app-content
|
||||||
|
{
|
||||||
|
// Limit max width
|
||||||
|
max-width: 1300px;
|
||||||
|
text-align: center;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.theme-default
|
.theme-default
|
||||||
{
|
{
|
||||||
--unread: #ff6c00;
|
--unread: #ff6c00;
|
||||||
@@ -15,4 +23,6 @@
|
|||||||
--assignment-type-2: #3f991e;
|
--assignment-type-2: #3f991e;
|
||||||
--assignment-type-3: #ff9900;
|
--assignment-type-3: #ff9900;
|
||||||
--assignment-type-4: #b02b02;
|
--assignment-type-4: #b02b02;
|
||||||
|
|
||||||
|
--font: 'Avenir', Helvetica, Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-53
@@ -8,6 +8,7 @@ import pWaitFor from 'p-wait-for';
|
|||||||
import {HttpUtils} from '@/utils/http-utils';
|
import {HttpUtils} from '@/utils/http-utils';
|
||||||
import {CourseUtils} from '@/utils/course-utils';
|
import {CourseUtils} from '@/utils/course-utils';
|
||||||
import {GPAUtils} from '@/utils/gpa-utils';
|
import {GPAUtils} from '@/utils/gpa-utils';
|
||||||
|
import Loading from '@/components/loading/loading.vue';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Objects of this interface represent assignment grades.
|
* Objects of this interface represent assignment grades.
|
||||||
@@ -15,6 +16,7 @@ import {GPAUtils} from '@/utils/gpa-utils';
|
|||||||
export interface Assignment
|
export interface Assignment
|
||||||
{
|
{
|
||||||
id: number,
|
id: number,
|
||||||
|
scoreId: number,
|
||||||
type: string,
|
type: string,
|
||||||
typeId: number,
|
typeId: number,
|
||||||
description: string,
|
description: string,
|
||||||
@@ -56,7 +58,7 @@ export interface Course
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {Login, Navigation, Overall},
|
components: {Login, Navigation, Overall, Loading},
|
||||||
})
|
})
|
||||||
export default class App extends Vue
|
export default class App extends Vue
|
||||||
{
|
{
|
||||||
@@ -78,8 +80,14 @@ export default class App extends Vue
|
|||||||
// Token
|
// Token
|
||||||
public token: string = '';
|
public token: string = '';
|
||||||
|
|
||||||
|
// Loading text
|
||||||
|
public loading: string = '';
|
||||||
|
|
||||||
|
// Loading error
|
||||||
|
public loadingError: boolean = false;
|
||||||
|
|
||||||
// Http Client
|
// Http Client
|
||||||
public http: HttpUtils = new HttpUtils('');
|
public static http: HttpUtils = new HttpUtils();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when the instance is created.
|
* This is called when the instance is created.
|
||||||
@@ -100,11 +108,14 @@ export default class App extends Vue
|
|||||||
// Hide login bar
|
// Hide login bar
|
||||||
this.showLogin = false;
|
this.showLogin = false;
|
||||||
|
|
||||||
|
// Show loading message
|
||||||
|
this.logLoading('1. Logging in...');
|
||||||
|
|
||||||
// Store token
|
// Store token
|
||||||
this.token = token;
|
this.token = token;
|
||||||
|
|
||||||
// Assign token to http client
|
// Assign token to http client
|
||||||
this.http.token = token;
|
App.http.token = token;
|
||||||
|
|
||||||
// Load data
|
// Load data
|
||||||
this.loadCoursesAfterLogin();
|
this.loadCoursesAfterLogin();
|
||||||
@@ -115,7 +126,11 @@ export default class App extends Vue
|
|||||||
*/
|
*/
|
||||||
public loadCoursesAfterLogin()
|
public loadCoursesAfterLogin()
|
||||||
{
|
{
|
||||||
this.http.post('/courses', {}).then(response =>
|
// Show loading message
|
||||||
|
this.logLoading('2. Loading courses...');
|
||||||
|
|
||||||
|
// Post request
|
||||||
|
App.http.post('/courses', {}).then(response =>
|
||||||
{
|
{
|
||||||
// Check success
|
// Check success
|
||||||
if (response.success)
|
if (response.success)
|
||||||
@@ -126,13 +141,9 @@ export default class App extends Vue
|
|||||||
// Load assignments
|
// Load assignments
|
||||||
this.loadAssignments();
|
this.loadAssignments();
|
||||||
}
|
}
|
||||||
else
|
else throw new Error(response.data);
|
||||||
{
|
|
||||||
// Show error message TODO: Show it properly
|
|
||||||
alert(response.data);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(alert);
|
.catch(e => this.showError(`Error: Course data failed to load.\n(${e})`));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,11 +151,14 @@ export default class App extends Vue
|
|||||||
*/
|
*/
|
||||||
public loadAssignments()
|
public loadAssignments()
|
||||||
{
|
{
|
||||||
|
// Show loading message
|
||||||
|
this.logLoading('3. Loading assignments...');
|
||||||
|
|
||||||
// Get assignments for all the courses
|
// Get assignments for all the courses
|
||||||
this.courses.forEach(course =>
|
this.courses.forEach(course =>
|
||||||
{
|
{
|
||||||
// Send request to get assignments
|
// Send request to get assignments
|
||||||
this.http.post('/assignments', {id: course.assignmentsId}).then(response =>
|
App.http.post('/assignments', {'assignmentsId': course.assignmentsId}).then(response =>
|
||||||
{
|
{
|
||||||
// Check success
|
// Check success
|
||||||
if (response.success)
|
if (response.success)
|
||||||
@@ -153,17 +167,13 @@ export default class App extends Vue
|
|||||||
// Parse json and filter it
|
// Parse json and filter it
|
||||||
course.assignments = JsonUtils.filterAssignments(response.data);
|
course.assignments = JsonUtils.filterAssignments(response.data);
|
||||||
}
|
}
|
||||||
else
|
else throw new Error(response.data);
|
||||||
{
|
|
||||||
// Show error message TODO: Show it properly
|
|
||||||
alert(response.data);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(alert);
|
.catch(e => this.showError(`Error: Assignments data failed to load.\n(${e})`));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for assignments to be ready.
|
// Wait for assignments to be ready.
|
||||||
pWaitFor(() => this.isAssignmentsReady()).then(() =>
|
pWaitFor(() => this.courses.every(c => c.assignments != null)).then(() =>
|
||||||
{
|
{
|
||||||
// Filter courses
|
// Filter courses
|
||||||
this.filteredCourses = CourseUtils.getGradedCourses(this.courses);
|
this.filteredCourses = CourseUtils.getGradedCourses(this.courses);
|
||||||
@@ -173,26 +183,14 @@ export default class App extends Vue
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Are assignments ready or not
|
|
||||||
*
|
|
||||||
* @returns boolean Ready or not
|
|
||||||
*/
|
|
||||||
private isAssignmentsReady(): boolean
|
|
||||||
{
|
|
||||||
for (const course of this.courses)
|
|
||||||
{
|
|
||||||
if (course.assignments == null) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the courses' grading algorithms. (Total-mean or percent-type)
|
* Check the courses' grading algorithms. (Total-mean or percent-type)
|
||||||
*/
|
*/
|
||||||
private checkGradingAlgorithms()
|
private checkGradingAlgorithms()
|
||||||
{
|
{
|
||||||
|
// Show loading message
|
||||||
|
this.logLoading('4. Checking grading algorithms...');
|
||||||
|
|
||||||
// Loop through all the courses
|
// Loop through all the courses
|
||||||
for (const course of this.filteredCourses)
|
for (const course of this.filteredCourses)
|
||||||
{
|
{
|
||||||
@@ -204,7 +202,7 @@ export default class App extends Vue
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Request grading scheme for this course
|
// Request grading scheme for this course
|
||||||
this.http.post('/grading', {'assignment_id': course.assignmentsId}).then(response =>
|
App.http.post('/grading', {'assignmentsId': course.assignmentsId}).then(response =>
|
||||||
{
|
{
|
||||||
// Check success
|
// Check success
|
||||||
if (response.success)
|
if (response.success)
|
||||||
@@ -212,41 +210,43 @@ export default class App extends Vue
|
|||||||
// Add it to course
|
// Add it to course
|
||||||
course.grading = response.data;
|
course.grading = response.data;
|
||||||
}
|
}
|
||||||
else
|
else throw new Error(response.data);
|
||||||
{
|
|
||||||
// Show error message TODO: Show it properly
|
|
||||||
alert(response.data)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(alert)
|
.catch(e => this.showError(`Error: Grading data failed to load.\n(${e})`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for done
|
// Wait for done
|
||||||
pWaitFor(() => this.isGradingReady()).then(() =>
|
pWaitFor(() => this.filteredCourses.every(c => c.grading != undefined)).then(() =>
|
||||||
{
|
{
|
||||||
// When the assignments are ready
|
// When the assignments are ready
|
||||||
// TODO: Display loading
|
|
||||||
this.assignmentsReady = true;
|
this.assignmentsReady = true;
|
||||||
|
|
||||||
|
// Remove loading
|
||||||
|
this.logLoading('');
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Are grading algorithms ready or not.
|
* Log a message to loading screen
|
||||||
*
|
*
|
||||||
* @returns boolean Ready or not
|
* @param message Message
|
||||||
*/
|
*/
|
||||||
private isGradingReady(): boolean
|
private logLoading(message: string)
|
||||||
{
|
{
|
||||||
for (const course of this.filteredCourses)
|
if (message == '') this.loading = '';
|
||||||
{
|
else this.loading += '\n' + message;
|
||||||
if (course.grading == undefined)
|
}
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
/**
|
||||||
|
* Show error message on loading screen
|
||||||
|
*
|
||||||
|
* @param message Error message
|
||||||
|
*/
|
||||||
|
private showError(message: string)
|
||||||
|
{
|
||||||
|
this.loadingError = true;
|
||||||
|
this.loading = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app" class="theme-default">
|
<div id="app" class="theme-default">
|
||||||
<login v-if="showLogin" v-on:login:token="onLogin" :http="http"></login>
|
<login v-if="showLogin" v-on:login:token="onLogin"></login>
|
||||||
<navigation :courses="filteredCourses"
|
<navigation :courses="filteredCourses"
|
||||||
v-on:sign-out="signOut()"
|
v-on:sign-out="signOut()"
|
||||||
v-on:navigation:select="onNavigate">
|
v-on:navigation:select="onNavigate">
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
v-if="selectedTab === 'overall' && assignmentsReady">
|
v-if="selectedTab === 'overall' && assignmentsReady">
|
||||||
</overall>
|
</overall>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<loading v-if="loading !== ''" :text="loading" :error="loadingError"></loading>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<div id="loading">
|
||||||
|
<div id="text" :class="message()">
|
||||||
|
{{message()}}
|
||||||
|
|
||||||
|
<div v-if="!error" class="el-loading-spinner">
|
||||||
|
<svg viewBox="25 25 50 50" class="circular">
|
||||||
|
<circle cx="50" cy="50" r="20" fill="none" class="path" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="error" id="error-details">
|
||||||
|
<span v-for="(line, index) in getText()" :style="`font-size: ${-index === 0 ? 16 : 12}px;`">
|
||||||
|
{{line}}
|
||||||
|
<br>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!error" id="details">
|
||||||
|
<span v-for="(line, index) in getText()" :style="`font-size: ${16 - getText().length + index}px;`">
|
||||||
|
{{line}}
|
||||||
|
<br>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class Loading extends Vue
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
@Prop() text: string;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
@Prop() error: boolean;
|
||||||
|
|
||||||
|
getText()
|
||||||
|
{
|
||||||
|
return this.text.split('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
message()
|
||||||
|
{
|
||||||
|
return this.error ? 'Error' : 'Loading';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#loading
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background: #00000065;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Error
|
||||||
|
{
|
||||||
|
color: #ffdddd !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#text
|
||||||
|
{
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
font-size: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#details
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
margin-top: -5px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#error-details
|
||||||
|
{
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-loading-spinner
|
||||||
|
{
|
||||||
|
top: unset !important;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
width: unset !important;
|
||||||
|
position: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-loading-spinner .path
|
||||||
|
{
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||||
import Constants from '@/constants';
|
import Constants from '@/constants';
|
||||||
import {HttpUtils} from '@/utils/http-utils';
|
import {HttpUtils} from '@/utils/http-utils';
|
||||||
|
import App from '@/components/app/app';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component handles user login, and obtains data from the server.
|
* This component handles user login, and obtains data from the server.
|
||||||
@@ -17,14 +18,18 @@ export default class Login extends Vue
|
|||||||
public loading: boolean = false;
|
public loading: boolean = false;
|
||||||
public error: String = '';
|
public error: String = '';
|
||||||
|
|
||||||
@Prop()
|
|
||||||
public http?: HttpUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when the instance is created.
|
* This is called when the instance is created.
|
||||||
*/
|
*/
|
||||||
public created()
|
public created()
|
||||||
{
|
{
|
||||||
|
// Check cookies version
|
||||||
|
if (!this.$cookies.isKey('va.version') || this.$cookies.get('va.version') != Constants.VERSION)
|
||||||
|
{
|
||||||
|
// Clear all cookies
|
||||||
|
this.$cookies.keys().forEach(key => this.$cookies.remove(key));
|
||||||
|
}
|
||||||
|
|
||||||
// Check login cookies
|
// Check login cookies
|
||||||
if (this.$cookies.isKey('va.token'))
|
if (this.$cookies.isKey('va.token'))
|
||||||
{
|
{
|
||||||
@@ -42,7 +47,7 @@ export default class Login extends Vue
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
// Fetch request
|
// Fetch request
|
||||||
(<HttpUtils> this.http).post('/login', {username: this.username, password: this.password})
|
App.http.post('/login', {username: this.username, password: this.password})
|
||||||
.then(response =>
|
.then(response =>
|
||||||
{
|
{
|
||||||
// Check success
|
// Check success
|
||||||
@@ -50,6 +55,7 @@ export default class Login extends Vue
|
|||||||
{
|
{
|
||||||
// Save token to cookies
|
// Save token to cookies
|
||||||
this.$cookies.set('va.token', response.data, '7d');
|
this.$cookies.set('va.token', response.data, '7d');
|
||||||
|
this.$cookies.set('va.version', Constants.VERSION);
|
||||||
|
|
||||||
// Call custom event with token
|
// Call custom event with token
|
||||||
this.$emit('login:token', response.data);
|
this.$emit('login:token', response.data);
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ export default class Constants
|
|||||||
*/
|
*/
|
||||||
public static API_URL: string = 'https://va.hydev.org/api';
|
public static API_URL: string = 'https://va.hydev.org/api';
|
||||||
|
|
||||||
public static VERSION: string = '0.3.2.452';
|
public static VERSION: string = '0.3.4.561';
|
||||||
|
|
||||||
public static GITHUB: string = 'https://github.com/HyDevelop/VeracrossAnalyzer.Client';
|
public static GITHUB: string = 'https://github.com/HyDevelop/VeracrossAnalyzer.Client';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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 CoursePage extends Vue
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
@Prop({required: true}) course: Course;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<div id="course-page">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./course-page.ts" lang="ts"></script>
|
||||||
|
<style src="./course-page.scss" lang="scss"></style>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
// Expansion color
|
// Expansion color
|
||||||
background: #f9f9f9;
|
background: #f4f6f9;
|
||||||
|
|
||||||
// Main card content
|
// Main card content
|
||||||
.course-card-content.main
|
.course-card-content.main
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||||
import {Assignment, Course} from '@/components/app/app';
|
import App, {Assignment, Course} from '@/components/app/app';
|
||||||
import {GPAUtils} from '@/utils/gpa-utils';
|
import {GPAUtils} from '@/utils/gpa-utils';
|
||||||
import Constants from '@/constants';
|
import Constants from '@/constants';
|
||||||
import UnreadEntry from '@/pages/overall/overall-course/unread-entry/unread-entry';
|
import UnreadEntry from '@/pages/overall/overall-course/unread-entry/unread-entry';
|
||||||
@@ -28,8 +28,25 @@ export default class OverallCourse extends Vue
|
|||||||
else return this.unread;
|
else return this.unread;
|
||||||
}
|
}
|
||||||
|
|
||||||
unreadTable()
|
/**
|
||||||
|
* Mark an assignment as read
|
||||||
|
*/
|
||||||
|
markAsRead(assignment: Assignment)
|
||||||
{
|
{
|
||||||
return [{date: '2019-10-04', name: 'Letter from a Penn Farmer Discussion Forum', grade: '14|14'}];
|
App.http.post('/mark-as-read', {scoreId: assignment.scoreId})
|
||||||
|
.then(response =>
|
||||||
|
{
|
||||||
|
// Check success
|
||||||
|
if (response.success)
|
||||||
|
{
|
||||||
|
this.unreadAssignments = this.unreadAssignments.filter(a => a != assignment);
|
||||||
|
this.unread = this.unreadAssignments.length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Show error message TODO: Show it properly
|
||||||
|
alert(response.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
v-if="countUnread() !== 0">
|
v-if="countUnread() !== 0">
|
||||||
<unread-entry v-for="assignment in unreadAssignments"
|
<unread-entry v-for="assignment in unreadAssignments"
|
||||||
:assignment="assignment"
|
:assignment="assignment"
|
||||||
:key="assignment.id">
|
:key="assignment.id"
|
||||||
|
v-on:mark-as-read="markAsRead">
|
||||||
</unread-entry>
|
</unread-entry>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
.unread-entry
|
.unread-entry
|
||||||
{
|
{
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0 20px 0 20px;
|
padding: 0 10px 0 20px;
|
||||||
background: #f5f7fa;
|
background: #f5f7fa;
|
||||||
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
// Date
|
// Date
|
||||||
.el-col.date
|
.el-col.date
|
||||||
{
|
{
|
||||||
|
min-width: 130px;
|
||||||
|
|
||||||
span.month
|
span.month
|
||||||
{
|
{
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
@@ -23,8 +25,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Description
|
||||||
.el-col.description
|
.el-col.description
|
||||||
{
|
{
|
||||||
|
width: unset;
|
||||||
|
|
||||||
span.type
|
span.type
|
||||||
{
|
{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -46,6 +51,7 @@
|
|||||||
.el-col.grade
|
.el-col.grade
|
||||||
{
|
{
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
float: right;
|
||||||
|
|
||||||
span.percent
|
span.percent
|
||||||
{
|
{
|
||||||
@@ -74,6 +80,14 @@
|
|||||||
background: #ddd;
|
background: #ddd;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark as read
|
||||||
|
button.mark-as-read
|
||||||
|
{
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #aaa;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-box
|
.entry-box
|
||||||
|
|||||||
@@ -14,8 +14,17 @@ export default class UnreadEntry extends Vue
|
|||||||
*
|
*
|
||||||
* @param date Date
|
* @param date Date
|
||||||
*/
|
*/
|
||||||
private getMoment(date: string)
|
getMoment(date: string)
|
||||||
{
|
{
|
||||||
return moment(new Date(date));
|
return moment(new Date(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this unread assignment as read
|
||||||
|
*/
|
||||||
|
markAsRead()
|
||||||
|
{
|
||||||
|
// Call custom event
|
||||||
|
this.$emit('mark-as-read', this.assignment)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="score entry-box">{{assignment.score}}</span>
|
<span class="score entry-box">{{assignment.score}}</span>
|
||||||
<span class="max entry-box">{{assignment.scoreMax}}</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">
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ export default class OverallLine extends Vue
|
|||||||
},
|
},
|
||||||
yAxis:
|
yAxis:
|
||||||
{
|
{
|
||||||
min: (value: any) => value.min - 10
|
min: (value: any) => value.min,
|
||||||
|
max: (value: any) => value.max
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
.gpa.text
|
.gpa.text
|
||||||
{
|
{
|
||||||
font-size: 35px;
|
font-size: 35px;
|
||||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
font-family: var(--font);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gpa.max
|
.gpa.max
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ export class HttpUtils
|
|||||||
{
|
{
|
||||||
public token: string = '';
|
public token: string = '';
|
||||||
|
|
||||||
constructor (token: string)
|
|
||||||
{
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public post(node: string, body: any): Promise<any>
|
public post(node: string, body: any): Promise<any>
|
||||||
{
|
{
|
||||||
// Add token
|
// Add token
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default class JsonUtils
|
|||||||
result.push(
|
result.push(
|
||||||
{
|
{
|
||||||
id: assignment.assignment_id,
|
id: assignment.assignment_id,
|
||||||
|
scoreId: assignment.score_id,
|
||||||
type: assignment.assignment_type,
|
type: assignment.assignment_type,
|
||||||
typeId: assignment.assignment_type_id,
|
typeId: assignment.assignment_type_id,
|
||||||
description: assignment.assignment_description,
|
description: assignment.assignment_description,
|
||||||
|
|||||||
Reference in New Issue
Block a user