Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 24befed17b | |||
| 07991b2a0e | |||
| 51ea0c7a80 | |||
| 8b01428208 | |||
| f69e2617d4 | |||
| ddfdb47b93 | |||
| 14849f4211 | |||
| 067c599cb1 | |||
| 33ceaa38c0 | |||
| 0a0288c2ee | |||
| 90f888bc4b | |||
| 9bb34fb2a4 | |||
| 22b75a6b30 | |||
| 6af8410698 | |||
| f023c724fa | |||
| 6bbbe9cece | |||
| e6a4a04bb4 | |||
| 5c4a391d96 | |||
| a75b15d840 | |||
| 1322bd6326 | |||
| e2997c345c | |||
| 923a7e824f | |||
| 10ac0b5330 | |||
| bfeba9da40 | |||
| 752a865334 | |||
| 5eda771070 | |||
| 0826080f82 | |||
| 6fab785a49 | |||
| ecbcca5f14 | |||
| 861de56f10 | |||
| 3ce66e1201 | |||
| 7e8ea73363 | |||
| 9142525d21 | |||
| 62577ff1bb | |||
| 953556ccee | |||
| cc8621f304 | |||
| baae26d244 | |||
| cbf70cbeef | |||
| 1e28191e67 | |||
| ef4d9d38e7 | |||
| 52626406bf | |||
| fec6c77dc2 | |||
| 4db8ee7837 | |||
| 9bfde0e39d |
@@ -71,3 +71,14 @@ div.el-card.course-card > div.el-card__body
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Non-selectable text
|
||||||
|
.unselectable
|
||||||
|
{
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,5 +46,46 @@
|
|||||||
// Font
|
// Font
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#next-course
|
||||||
|
{
|
||||||
|
// Down center
|
||||||
|
width: 50%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 25%;
|
||||||
|
padding-top: 2px;
|
||||||
|
box-shadow: 0 -2px 9px 0 #ecebeb;
|
||||||
|
|
||||||
|
// Trapezoid
|
||||||
|
transform: perspective(50px) rotateX(10deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#prev-course
|
||||||
|
{
|
||||||
|
// Up center
|
||||||
|
width: 50%;
|
||||||
|
position: absolute;
|
||||||
|
top: 61px;
|
||||||
|
left: 25%;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
box-shadow: 0 2px 9px 0 #ecebeb;
|
||||||
|
|
||||||
|
// Trapezoid
|
||||||
|
transform: perspective(50px) rotateX(-10deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-course-operations
|
||||||
|
{
|
||||||
|
// Background
|
||||||
|
background-color: rgba(214, 214, 214, 0.67);
|
||||||
|
opacity: 0.85;
|
||||||
|
|
||||||
|
// Font
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ab8585;
|
||||||
|
|
||||||
|
// Cursor
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {Component, Prop, Vue} from 'vue-property-decorator';
|
|||||||
import Constants from '@/constants';
|
import Constants from '@/constants';
|
||||||
import {Course} from '@/components/app/app';
|
import {Course} from '@/components/app/app';
|
||||||
import {CourseUtils} from '@/utils/course-utils';
|
import {CourseUtils} from '@/utils/course-utils';
|
||||||
|
import {FormatUtils} from '@/utils/format-utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component is the top navigation bar
|
* This component is the top navigation bar
|
||||||
@@ -14,20 +15,27 @@ export default class Navigation extends Vue
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@Prop() activeIndex: string;
|
@Prop() activeIndex: string;
|
||||||
|
|
||||||
@Prop() courses: any;
|
// @ts-ignore
|
||||||
|
@Prop() courses: Course[];
|
||||||
|
|
||||||
|
// Instance
|
||||||
|
public static instance: Navigation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when the instance is created.
|
* This is called when the instance is created.
|
||||||
*/
|
*/
|
||||||
public created()
|
public created()
|
||||||
{
|
{
|
||||||
|
// Set instance
|
||||||
|
Navigation.instance = this;
|
||||||
|
|
||||||
// Set history state
|
// Set history state
|
||||||
let url = window.location.pathname;
|
let url = window.location.pathname;
|
||||||
if (url == '/' || url == '') url = '/overall';
|
if (url == '/' || url == '') url = '/overall';
|
||||||
window.history.replaceState({lastTab: url.substring(1)}, '', url);
|
window.history.replaceState({lastTab: url.substring(1)}, '', url);
|
||||||
|
|
||||||
// Update initial index
|
// Update initial index
|
||||||
this.updateIndex(url.substring(1));
|
this.updateIndex(url.substring(1), false);
|
||||||
|
|
||||||
// Create history state listener
|
// Create history state listener
|
||||||
window.onpopstate = e =>
|
window.onpopstate = e =>
|
||||||
@@ -36,7 +44,7 @@ export default class Navigation extends Vue
|
|||||||
{
|
{
|
||||||
// Restore previous tab
|
// Restore previous tab
|
||||||
console.log(`onPopState: Current: ${this.activeIndex}, Previous: ${e.state.lastTab}`);
|
console.log(`onPopState: Current: ${this.activeIndex}, Previous: ${e.state.lastTab}`);
|
||||||
this.updateIndex(e.state.lastTab);
|
this.updateIndex(e.state.lastTab, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -56,25 +64,87 @@ export default class Navigation extends Vue
|
|||||||
{
|
{
|
||||||
// Update active index
|
// Update active index
|
||||||
this.updateIndex(index);
|
this.updateIndex(index);
|
||||||
|
|
||||||
// Debug output TODO: Remove this
|
|
||||||
console.log(`onNavigate: Previous: ${this.activeIndex}, New: ${index}`);
|
|
||||||
|
|
||||||
// Check url
|
|
||||||
let url = `/${index}`;
|
|
||||||
|
|
||||||
// Push history state
|
|
||||||
window.history.pushState({lastTab: index}, '', url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update index
|
* Update index
|
||||||
*
|
*
|
||||||
* @param newIndex New index
|
* @param newIndex New index
|
||||||
|
* @param history Record in history or not (Default true)
|
||||||
*/
|
*/
|
||||||
private updateIndex(newIndex: string)
|
public updateIndex(newIndex: string, history?: boolean)
|
||||||
{
|
{
|
||||||
|
// Call custom event
|
||||||
this.$emit('update:activeIndex', newIndex);
|
this.$emit('update:activeIndex', newIndex);
|
||||||
|
|
||||||
|
// 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}`;
|
||||||
|
|
||||||
|
// Push history state
|
||||||
|
window.history.pushState({lastTab: newIndex}, '', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update title
|
||||||
|
document.title = 'Veracross Analyzer - ' + this.getTitle(newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get title for index
|
||||||
|
*
|
||||||
|
* @param index Index
|
||||||
|
*/
|
||||||
|
public getTitle(index: string)
|
||||||
|
{
|
||||||
|
// Course
|
||||||
|
if (index.startsWith('course'))
|
||||||
|
{
|
||||||
|
return this.findCourse(index.split('/')[1], 0).name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Others
|
||||||
|
return FormatUtils.toTitleCase(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move to the next course
|
||||||
|
*
|
||||||
|
* @param indexOffset Index offset (Eg. 1 for next)
|
||||||
|
*/
|
||||||
|
public nextCourse(indexOffset: number)
|
||||||
|
{
|
||||||
|
// Set tab to the next index
|
||||||
|
this.updateIndex(CourseUtils.formatTabIndex(this.findNextCourse(indexOffset)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the next course
|
||||||
|
*
|
||||||
|
* @param indexOffset Index offset (Eg. 1 for next)
|
||||||
|
*/
|
||||||
|
public findNextCourse(indexOffset: number)
|
||||||
|
{
|
||||||
|
return this.findCourse(this.activeIndex.split('/')[1], indexOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find course
|
||||||
|
*
|
||||||
|
* @param courseId Course ID
|
||||||
|
* @param indexOffset Index offset (Eg. 1 for next)
|
||||||
|
*/
|
||||||
|
public findCourse(courseId: string, indexOffset: number)
|
||||||
|
{
|
||||||
|
// Find current course index
|
||||||
|
let courseIndex = this.courses.findIndex(c => c.id == +courseId);
|
||||||
|
|
||||||
|
// Find next course
|
||||||
|
return this.courses[courseIndex + indexOffset];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,7 +18,14 @@
|
|||||||
|
|
||||||
<el-button @click="signOut" id="sign-out-button" type="text">Sign Out</el-button>
|
<el-button @click="signOut" id="sign-out-button" type="text">Sign Out</el-button>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
<div class="line"></div>
|
<div v-if="activeIndex.includes('course') && findNextCourse(-1) != null"
|
||||||
|
@click="nextCourse(-1)" id="prev-course" class="nav-course-operations unselectable">
|
||||||
|
▲ PREVIOUS COURSE ▲
|
||||||
|
</div>
|
||||||
|
<div v-if="activeIndex.includes('course') && findNextCourse(1) != null"
|
||||||
|
@click="nextCourse(1)" id="next-course" class="nav-course-operations unselectable">
|
||||||
|
▼ NEXT COURSE ▼
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,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';
|
||||||
|
|
||||||
/** Current version */
|
/** Current version */
|
||||||
public static VERSION: string = '0.3.5.697';
|
public static VERSION: string = '0.3.6.741';
|
||||||
|
|
||||||
/** Minimum version that still supports the same cookies */
|
/** Minimum version that still supports the same cookies */
|
||||||
public static MIN_SUPPORTED_VERSION: string = '0.3.4.561';
|
public static MIN_SUPPORTED_VERSION: string = '0.3.4.561';
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import moment from 'moment';
|
|||||||
})
|
})
|
||||||
export default class CourseScatter extends Vue
|
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
|
// @ts-ignore
|
||||||
@Prop({required: true}) course: Course;
|
@Prop({required: true}) course: Course;
|
||||||
|
|
||||||
@@ -30,6 +32,7 @@ export default class CourseScatter extends Vue
|
|||||||
// Map assignments
|
// Map assignments
|
||||||
let map = this.mapAssignments();
|
let map = this.mapAssignments();
|
||||||
|
|
||||||
|
// Scatter data point style
|
||||||
let itemStyle =
|
let itemStyle =
|
||||||
{
|
{
|
||||||
normal:
|
normal:
|
||||||
@@ -94,9 +97,13 @@ export default class CourseScatter extends Vue
|
|||||||
axisPointer:
|
axisPointer:
|
||||||
{
|
{
|
||||||
type: 'cross'
|
type: 'cross'
|
||||||
}
|
},
|
||||||
|
formatter: (ps: any[]) => ps[0].data[0] + '<br>' + ps.map(p =>
|
||||||
|
`${CourseScatter.DOT.replace('{color}', p.color)}
|
||||||
|
${FormatUtils.limit(p.data[2], 22)}: ${p.data[1]}%<br>`).join('')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Legend
|
||||||
legend:
|
legend:
|
||||||
{
|
{
|
||||||
bottom: 24,
|
bottom: 24,
|
||||||
@@ -152,6 +159,6 @@ export default class CourseScatter extends Vue
|
|||||||
private static assignmentsData(assignments: Assignment[])
|
private static assignmentsData(assignments: Assignment[])
|
||||||
{
|
{
|
||||||
return assignments.filter(a => a.complete == 'Complete')
|
return assignments.filter(a => a.complete == 'Complete')
|
||||||
.map(a => [FormatUtils.toChartDate(a.date), (a.score / a.scoreMax * 100).toFixed(2)]);
|
.map(a => [FormatUtils.toChartDate(a.date), (a.score / a.scoreMax * 100).toFixed(2), a.description]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {Component, Prop, Vue} from 'vue-property-decorator';
|
|||||||
import {Course} from '@/components/app/app';
|
import {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 {FormatUtils} from '@/utils/format-utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
})
|
})
|
||||||
@@ -40,7 +41,7 @@ export default class OverallBar extends Vue
|
|||||||
rotate: 90,
|
rotate: 90,
|
||||||
|
|
||||||
// Truncate text length
|
// Truncate text length
|
||||||
formatter: (value: string) => value.length <= 16 ? value : value.substr(0, 14) + '...'
|
formatter: (value: string) => FormatUtils.limit(value, 16)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||||
import App, {Course} from '@/components/app/app';
|
import App, {Course} from '@/components/app/app';
|
||||||
import {CourseUtils} from '@/utils/course-utils';
|
import {CourseUtils} from '@/utils/course-utils';
|
||||||
|
import Navigation from '@/components/navigation/navigation';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {}
|
components: {}
|
||||||
@@ -48,7 +49,7 @@
|
|||||||
redirect()
|
redirect()
|
||||||
{
|
{
|
||||||
if (!this.clickable) return;
|
if (!this.clickable) return;
|
||||||
App.instance.selectedTab = CourseUtils.formatTabIndex(this.course);
|
Navigation.instance.updateIndex(CourseUtils.formatTabIndex(this.course));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -15,4 +15,26 @@ export class FormatUtils
|
|||||||
// Convert to yyyy-mm-dd
|
// Convert to yyyy-mm-dd
|
||||||
return moment(date).format('YYYY-MM-DD');
|
return moment(date).format('YYYY-MM-DD');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit string length
|
||||||
|
*
|
||||||
|
* @param str String
|
||||||
|
* @param length Max length
|
||||||
|
*/
|
||||||
|
public static limit(str: string, length: number): string
|
||||||
|
{
|
||||||
|
return str.length <= length ? str : str.substr(0, length - 2) + '...'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To Title Case
|
||||||
|
*
|
||||||
|
* @param str oRigInAL sTrING
|
||||||
|
* @return string Original String
|
||||||
|
*/
|
||||||
|
public static toTitleCase(str: string)
|
||||||
|
{
|
||||||
|
return str.replace(/\w\S*/g, s => s.charAt(0).toUpperCase() + s.substr(1).toLowerCase())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user