Compare commits
453 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86ed26afeb | |||
| 35c4147cc1 | |||
| 711e69ddfb | |||
| 90d9cac19a | |||
| 61593d7d23 | |||
| 7c41e7adcc | |||
| 0215be8681 | |||
| 1139384ab3 | |||
| 45174422e9 | |||
| 2077d784fd | |||
| fa341101a8 | |||
| 1959ac589d | |||
| 5744493ac3 | |||
| 64eb4530af | |||
| d38fdfaced | |||
| f76484f824 | |||
| ef6adb0a27 | |||
| ca80df9541 | |||
| 828907120b | |||
| 460074b18a | |||
| 104bd4498e | |||
| 97082b1e55 | |||
| c9561fba71 | |||
| 8aa913fe09 | |||
| a598521491 | |||
| 6461456c16 | |||
| b4f697bdea | |||
| 721295b9d1 | |||
| 70c6e74623 | |||
| 1348ca27ab | |||
| 116592e436 | |||
| 19284a4ddf | |||
| 35e603b850 | |||
| 882c7bb35e | |||
| 9c8ce3a7f2 | |||
| 8a6af65786 | |||
| 4633bd902c | |||
| 10fcb8a2f6 | |||
| 3ad47eddfd | |||
| 636f1474bd | |||
| ebc9fac4dc | |||
| a0e5db6ed9 | |||
| f8acd8e222 | |||
| 7b21f82024 | |||
| 240cd7ce69 | |||
| 8a4f9c6f79 | |||
| 16f5865c4c | |||
| 25c3d8e6c1 | |||
| e1584c80a5 | |||
| 89dc493edf | |||
| aa59075939 | |||
| c55beaf30d | |||
| 67f1343744 | |||
| 6b0e0fc8e8 | |||
| af41ad5f53 | |||
| 622cd524e5 | |||
| 7266cf0d80 | |||
| 47fc11ed96 | |||
| fd927991c7 | |||
| 4b5e6d9856 | |||
| 3aa5ef9c95 | |||
| ded76b774c | |||
| 1bcf2f7410 | |||
| d8cce9ae11 | |||
| b4f29559cf | |||
| a7ea833860 | |||
| 9c54033343 | |||
| 7b9dab9d78 | |||
| 7f094cbafb | |||
| c4b8542b5b | |||
| 0345d37b2c | |||
| 97818c45f5 | |||
| 1a09c7311e | |||
| b658f6f707 | |||
| 1d63e4865b | |||
| 78fbd342d9 | |||
| 141c192017 | |||
| 87e44b83f7 | |||
| 1064831379 | |||
| 1e42ef7873 | |||
| c8327f3774 | |||
| 69cd4ff050 | |||
| 027f8bfd34 | |||
| 70ede22c18 | |||
| fd74298b0b | |||
| d47bae00ff | |||
| 0c91b1b598 | |||
| 6ba789c7cf | |||
| 54b70f6a1c | |||
| ac32a02ff4 | |||
| 993578277e | |||
| 48fb8c7d4c | |||
| 0f6f27aa15 | |||
| b2947e9ef6 | |||
| c5ef8a297b | |||
| 02ca6b8d47 | |||
| 98b92b4dfb | |||
| 1c2afdfa52 | |||
| 55b4bd9173 | |||
| e422c03ca5 | |||
| 831ef16a2f | |||
| 0deb0ed719 | |||
| f3d44ceda2 | |||
| cb11db6201 | |||
| b52f3d2617 | |||
| 39cd5bf1a2 | |||
| 19f03717bc | |||
| 6d728c25ae | |||
| c8daa2a06e | |||
| 4d41e679dd | |||
| acfebf9ff3 | |||
| 5af1b1e5a1 | |||
| bc9a4f00c1 | |||
| f13f3a155c | |||
| a19edb8aa7 | |||
| 6a0500acb2 | |||
| 96af69508f | |||
| 3fbc9764e9 | |||
| a79dcb76af | |||
| 86c2fd5df7 | |||
| 07eaeef133 | |||
| d1bd0a5667 | |||
| 49dad14c84 | |||
| 50e3c345d5 | |||
| a444dbb32a | |||
| 03e212bd37 | |||
| 0325eb13d5 | |||
| bfcb80aa64 | |||
| 91a6c0c6e4 | |||
| 845cc94513 | |||
| 21ccaceb53 | |||
| b9f63171f2 | |||
| 636d92d1a0 | |||
| 199ab9e00c | |||
| 77541764d1 | |||
| 53ae74f9c4 | |||
| 5bc1d6ba48 | |||
| 831ddcf84e | |||
| 35a85d8e83 | |||
| 71b0a6e4dd | |||
| d4fbd04466 | |||
| 31b3814b1e | |||
| 1aff2b0a68 | |||
| 2567fcadbd | |||
| d9e0e9f84e | |||
| 59b31ea43f | |||
| 2c8b3e0f84 | |||
| 6910a7b5ea | |||
| cbdcfc4ca1 | |||
| feabc336c1 | |||
| b5b9f14a49 | |||
| fc93cd1248 | |||
| 6e58c634a1 | |||
| 10cca344c7 | |||
| cdf58ea3a0 | |||
| d97c80afbb | |||
| 8c7d028f5b | |||
| 19ba8ec7e6 | |||
| 6937952f3a | |||
| 485717d3ef | |||
| 3f3e46bec5 | |||
| 1494315e0c | |||
| 76eb8168ce | |||
| e4a1c4973d | |||
| b22fc41582 | |||
| 0e1264d938 | |||
| a94a6db4b7 | |||
| 18b5b7de77 | |||
| 28d4e5d871 | |||
| cc5b7b2793 | |||
| 7b4ced4e94 | |||
| 70e6debe82 | |||
| e0c4426ebb | |||
| 495e776971 | |||
| fc625c8edb | |||
| 6c1623cec5 | |||
| a9a9cf5f11 | |||
| d8f276005d | |||
| 5c9e67478f | |||
| 5e0394f11c | |||
| 774e436c9c | |||
| 8b653e3317 | |||
| 9731d4e380 | |||
| e3ca21ec29 | |||
| a89c29dc53 | |||
| 87a59416ec | |||
| 8243312179 | |||
| 3dcc1b8319 | |||
| 899ff22677 | |||
| 788f51399a | |||
| 575dbdf765 | |||
| 1659964011 | |||
| 02533f6b07 | |||
| 68a91e5eb8 | |||
| 1eb6128aac | |||
| fbd59f9ec3 | |||
| 7eb7838f66 | |||
| 6459e1c09b | |||
| 4461ef835f | |||
| 6ee207d754 | |||
| 3c0c0d5f1b | |||
| bc8fcc021d | |||
| e9a6cfe5c5 | |||
| 0e92cedf54 | |||
| a73d6b0399 | |||
| 8042765ff5 | |||
| 74a033fb5d | |||
| cc4378d905 | |||
| 0fd3696342 | |||
| 3de4b1f6e6 | |||
| 610d1d190d | |||
| c262584e2f | |||
| a526946b83 | |||
| b78c5b4f5b | |||
| 8978b7ca7b | |||
| d254706a21 | |||
| 8dd7d35abe | |||
| 2b989fb137 | |||
| e2a13e90e0 | |||
| 3546a57711 | |||
| 770e26b0cf | |||
| fff60f5754 | |||
| a46e011c90 | |||
| 48224d9e34 | |||
| eb311e9f2d | |||
| 7b53e65a1b | |||
| 065d6d31a3 | |||
| d8eb160123 | |||
| 8e924d8e33 | |||
| 1e45b418b8 | |||
| e42c8be76f | |||
| b6b3f921a2 | |||
| d3234be6db | |||
| 50a5f9dcc5 | |||
| 62d40dd0a7 | |||
| 9e8e44b91f | |||
| df85e1084f | |||
| 0b41f8ac5d | |||
| 7b11537e0f | |||
| 830f55441c | |||
| 0a49d791ae | |||
| c675fc5650 | |||
| ad8496e661 | |||
| 72b9e4d214 | |||
| fa3fc44526 | |||
| 419cced592 | |||
| 352fd58fb5 | |||
| f043d77e5e | |||
| 9e88f3efe1 | |||
| cadd328958 | |||
| 8c4759d497 | |||
| ac8f488ab4 | |||
| df639a360c | |||
| 24328508c0 | |||
| ebd4849703 | |||
| 80d72bb5c0 | |||
| 204e7dc04a | |||
| cef0b31dba | |||
| 22ba3acab0 | |||
| 88bdcd8cf8 | |||
| 8559a93b9c | |||
| f8db85dca3 | |||
| b4bd24d884 | |||
| f6d30a750a | |||
| 988d6f5463 | |||
| 608132ad6c | |||
| 6cffb745a7 | |||
| 82212cf10c | |||
| 9656b3184b | |||
| 08b665a1fa | |||
| 500e13ef9d | |||
| b95220ba1a | |||
| b8dfb8f732 | |||
| 002aa84444 | |||
| e7c513695d | |||
| e51dbd2c5b | |||
| 9157232d45 | |||
| 17ef8f4380 | |||
| c31dbf0e50 | |||
| 9c2c1c3195 | |||
| 820c3c1148 | |||
| 6efb832212 | |||
| c885137ed7 | |||
| bb4b34722f | |||
| 2beca45e38 | |||
| cbec0add3b | |||
| 0ebce968b9 | |||
| 4095041925 | |||
| 0e618ceb13 | |||
| 042e72abb6 | |||
| 517235982b | |||
| 6e7041edcd | |||
| 67cf33b48c | |||
| 4c822fd207 | |||
| 68afbb8c76 | |||
| 5da0e89e08 | |||
| b2db05d5e2 | |||
| 3cb74083a7 | |||
| d3072ccaf6 | |||
| d566b53c22 | |||
| 7bad961f70 | |||
| 13e307f8d2 | |||
| 448e699cd3 | |||
| 1ca32b5ebd | |||
| 5ac3183ec1 | |||
| a7384753c8 | |||
| 393fc1cc71 | |||
| 06c265159b | |||
| 53a0884d0b | |||
| 7b0f11a1f4 | |||
| 4dc5966e51 | |||
| be0a657ba3 | |||
| 3287c14fa3 | |||
| 7af20f806b | |||
| bd8d7fd113 | |||
| 76cf8c4c53 | |||
| eaa1609b77 | |||
| 1324afe978 | |||
| e86d2fd4f5 | |||
| cf34db2c61 | |||
| 9038a73678 | |||
| 446ed686bd | |||
| 4782870d94 | |||
| 3ce21623d8 | |||
| 2338e4f6af | |||
| 38089c74b5 | |||
| 8860c88b1a | |||
| 75f9dc9849 | |||
| 805ffaa50e | |||
| c7d16a00e6 | |||
| 67ec2b85b2 | |||
| 9a752305e3 | |||
| c95a5b343e | |||
| 5029555c21 | |||
| 8eb2080f14 | |||
| 0296b2151a | |||
| 3575db8182 | |||
| 0025ec9213 | |||
| 489328c624 | |||
| 36a211e186 | |||
| d7c4c87959 | |||
| 3cd9db65ec | |||
| e03798af13 | |||
| 1fd4e89f84 | |||
| c05b799334 | |||
| b9f0316f76 | |||
| 2b3cbb4061 | |||
| 0653deba64 | |||
| 67a9417f2b | |||
| ca49223432 | |||
| 6d0d23d0dd | |||
| 6c9061d4d9 | |||
| 56b9be01ba | |||
| c8935c41cb | |||
| 1d96dcc0a8 | |||
| 811de8f5e2 | |||
| ec4196088a | |||
| 863fdcb50e | |||
| 2e27fcceb9 | |||
| 84f202ac9e | |||
| fba56b3eca | |||
| a3c2bf6139 | |||
| 9997080c63 | |||
| 497875d56a | |||
| 79b50cc59c | |||
| 9c474d4ecd | |||
| 32e616b65d | |||
| c19581e602 | |||
| 91929df865 | |||
| fdeab0f2c9 | |||
| 3a7c447058 | |||
| 9bc1618dc5 | |||
| 08e98c140f | |||
| 70346aeece | |||
| 2a69d9a1de | |||
| ecb418cc61 | |||
| 21850bca5d | |||
| a574dcd64a | |||
| 06707403b7 | |||
| c0d4eab637 | |||
| f1955b61ab | |||
| 1a50ada15a | |||
| 0043a16666 | |||
| 7562a0144c | |||
| c22e38033e | |||
| 294480418c | |||
| fac4c86b2d | |||
| 7aebde42b1 | |||
| 10c994d6c2 | |||
| f82d1081f9 | |||
| d33e189934 | |||
| 54f862ff13 | |||
| 8cca38b55a | |||
| 22f3e208d8 | |||
| 615e4fa2cb | |||
| c8b6292cb1 | |||
| ebf9b0efc6 | |||
| db0915ca0e | |||
| 36b8857191 | |||
| 1dcb26cd25 | |||
| 822a1fecc2 | |||
| 7e73d6581b | |||
| 69b93d4356 | |||
| 06f3c9614a | |||
| 8ef5993f56 | |||
| 70d0ee6c29 | |||
| 5bc3964649 | |||
| aa64b2b4cd | |||
| e7daac738d | |||
| 6b15b617cf | |||
| 96f73cf31d | |||
| ff82c3811f | |||
| c4561d11a1 | |||
| 7216cdb5c8 | |||
| 2e697904c9 | |||
| 4be24d59fd | |||
| 53668f3cc2 | |||
| 6d20b42447 | |||
| 011db90677 | |||
| f7a43b3d86 | |||
| d9dfef59a2 | |||
| cea3c2724f | |||
| 19dfbd04b9 | |||
| e4bd1f7d50 | |||
| ec467dc5e8 | |||
| 2a723a5b06 | |||
| 4a89b3c24c | |||
| 2fa6f46869 | |||
| 1cd30558ff | |||
| b36336e024 | |||
| 80409bd753 | |||
| 851503edc6 | |||
| 6da21fbced | |||
| b0d6d78a7e | |||
| ad7bd06b13 | |||
| 0d194dea06 | |||
| b0d35b5551 | |||
| a257675a36 | |||
| 189984d900 | |||
| e51c0ac115 | |||
| f0dff3c3f9 | |||
| 162b915911 | |||
| ed1b843597 | |||
| c3419b9764 | |||
| b687c50cfb | |||
| 9cd0ba01f8 | |||
| 634db4c906 | |||
| 1524303486 | |||
| 7133ac7f15 | |||
| 0e60153cc8 | |||
| 9941dc95fe | |||
| 2e6b573d38 | |||
| 11573cb673 |
+21
@@ -0,0 +1,21 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,14 +0,0 @@
|
||||
<!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>
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 HyDEV
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,56 @@
|
||||
<h1 align="center"><br><br>
|
||||
VeracrossAnalyzer UI
|
||||
</h1>
|
||||
|
||||
<h4 align="center">
|
||||
A Website, A Visual Representation of Students' Grade Data on Veracross
|
||||
</h4>
|
||||
|
||||
<h5 align="center">
|
||||
<a href="#intro">Introduction</a>
|
||||
<a href="#setup">Project Setup</a>
|
||||
<a href="#license">License</a>
|
||||
</h5><br><br><br>
|
||||
|
||||
|
||||
|
||||
<a name="intro"></a>
|
||||
Introduction:
|
||||
--------
|
||||
|
||||
This is a website that generates visual representation of students' grade data on Veracross. Currently there is only one graph and one numerical data representing the GPA. But also it just released yesterday! (Yay!) What do you expect this soon lol?
|
||||
|
||||
**Here's how it looks like right now:** *(Now all of you know my grades ;-;)*
|
||||
|
||||

|
||||
|
||||
<br>
|
||||
|
||||
<a name="setup"></a>
|
||||
Project Setup:
|
||||
--------
|
||||
|
||||
TODO: Actually write a project setup tutorial that's not generated by Vue on initialization ;-;.
|
||||
|
||||
### Install
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
<a name="license"></a>
|
||||
License: [MIT](https://choosealicense.com/licenses/mit/)
|
||||
--------
|
||||
|
||||
The MIT license basically means that this project is open-soucred and you can do whatever you want with it, as long as you include a copy of this license in your distribution. You don't have to ask for permissions to use or anything. However, if you do bad things with it, I'm not responsible.
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
]
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"success":true,"data":{"assignments":[{"score_id":5593330,"id":322150,"assignment_id":322150,"assignment_type_id":3,"assignment_type":"Quiz","assignment_type_sort_key":3,"assignment_description":"2.3 Open Notes","grading_period":"Quarter 1","assignment_date_long":"Wed, Sep 11","due_date_long":"Wed, Sep 11","due_date":"Sep 11","due_day":"Wed","_date":"09/11/2019","include_in_calculated_grade":1,"num_attachments":0,"num_criteria":0,"num_feedback":0,"maximum_score":16,"points_possible":16,"raw_score":"14","percent_grade":"8750%","completion_status_id":3,"completion_status":"Complete","is_unread":1.0,"is_notification":0,"is_problem":0,"display_grade":1,"display_score":1,"display_maximum_score":1,"display_percent_grade":1,"display_points_possible":1,"allow_student_feedback":0},{"score_id":5584935,"id":321649,"assignment_id":321649,"assignment_type_id":3,"assignment_type":"Quiz","assignment_type_sort_key":3,"assignment_description":"2.2 Open Notes","grading_period":"Quarter 1","assignment_date_long":"Mon, Sep 09","due_date_long":"Mon, Sep 09","due_date":"Sep 09","due_day":"Mon","_date":"09/09/2019","include_in_calculated_grade":1,"num_attachments":0,"num_criteria":0,"num_feedback":0,"maximum_score":10,"points_possible":10,"raw_score":"6","percent_grade":"6000%","completion_status_id":3,"completion_status":"Complete","is_notification":0,"is_problem":0,"display_grade":1,"display_score":1,"display_maximum_score":1,"display_percent_grade":1,"display_points_possible":1,"allow_student_feedback":0},{"score_id":5602940,"id":322723,"assignment_id":322723,"assignment_type_id":2,"assignment_type":"Homework","assignment_type_sort_key":2,"assignment_description":"Autobiography","grading_period":"Quarter 1","assignment_date_long":"Wed, Sep 04","due_date_long":"Thu, Sep 05","due_date":"Sep 05","due_day":"Thu","_date":"09/05/2019","include_in_calculated_grade":1,"num_attachments":0,"num_criteria":0,"num_feedback":0,"maximum_score":10,"points_possible":10,"raw_score":"","percent_grade":"0%","completion_status_id":0,"completion_status":"Pending","is_notification":0,"is_problem":0,"display_grade":1,"display_score":1,"display_maximum_score":1,"display_percent_grade":1,"display_points_possible":1,"allow_student_feedback":0}],"attachments":[],"criteria":[],"criteria_grade_scale_levels":[]}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"success":true,"data":{"assignments":[],"attachments":[],"criteria":[],"criteria_grade_scale_levels":[]}}
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"success":true,"data":{"assignments":[],"attachments":[],"criteria":[],"criteria_grade_scale_levels":[]}}
|
||||
@@ -1 +0,0 @@
|
||||
{"success":true,"data":{"assignments":[],"attachments":[],"criteria":[],"criteria_grade_scale_levels":[]}}
|
||||
@@ -1 +0,0 @@
|
||||
{"success":true,"data":{"assignments":[],"attachments":[],"criteria":[],"criteria_grade_scale_levels":[]}}
|
||||
@@ -1 +0,0 @@
|
||||
{"success":true,"data":{"assignments":[],"attachments":[],"criteria":[],"criteria_grade_scale_levels":[]}}
|
||||
@@ -1,250 +0,0 @@
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": 1,
|
||||
"schoolPersonPk": 109467,
|
||||
"username": "ygui21",
|
||||
"lastLogin": "Jun 5, 2020 1:28:51 PM",
|
||||
"firstLogin": "Dec 2, 2019 7:31:15 PM",
|
||||
"firstName": "Yijie",
|
||||
"lastName": "Gui",
|
||||
"graduationYear": 2021,
|
||||
"emails": "ygui21@stjohnsprep.org",
|
||||
"classes": "32451|32453|32458|32856|32872|32874|32878|32880|32882|32890|33070|33093|33121|33173|33337|33464|34174|34197|34199|34209"
|
||||
},
|
||||
"token": "Removed",
|
||||
"courses": [
|
||||
{
|
||||
"level": "H",
|
||||
"id_ci": 196,
|
||||
"rating": {
|
||||
"id_ci": 196,
|
||||
"id_user": 1,
|
||||
"userFullName": "Yijie]\u003d[Gui",
|
||||
"ratings": [
|
||||
5,
|
||||
5,
|
||||
3,
|
||||
5,
|
||||
4
|
||||
],
|
||||
"comment": "Mr. Crowell\u0027s selected books for this course are very interesting, and the essay topics are generally unique too, allowing us to express our creativity. His lectures are also very in-depth, strengthening our understanding of the ideas that the authors wanted to express. However, the style of the classroom might be boring for some people. For grading fairness, I think it is very fair, but his standards are a little bit too high because achieving a perfect score for an essay is very much impossible."
|
||||
},
|
||||
"name": "English 3 H",
|
||||
"teacherName": "Mr. Crowell",
|
||||
"id": 33337,
|
||||
"assignmentsId": 10934147,
|
||||
"letterGrade": "A+",
|
||||
"numericGrade": 97.14,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "AP",
|
||||
"id_ci": 19,
|
||||
"rating": {
|
||||
"id_ci": 19,
|
||||
"id_user": -1,
|
||||
"userFullName": "Anonymous]\u003d[Student",
|
||||
"ratings": [
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
5
|
||||
],
|
||||
"comment": "Calculus made so much easier"
|
||||
},
|
||||
"name": "AP Calculus AB (Juniors)",
|
||||
"teacherName": "Ms. Dobrowolski",
|
||||
"id": 32453,
|
||||
"assignmentsId": 10934142,
|
||||
"letterGrade": "A+",
|
||||
"numericGrade": 100.0,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "AP",
|
||||
"id_ci": 251,
|
||||
"rating": {
|
||||
"id_ci": 251,
|
||||
"id_user": 1,
|
||||
"userFullName": "Yijie]\u003d[Gui",
|
||||
"ratings": [
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
5
|
||||
],
|
||||
"comment": "Cations go meow"
|
||||
},
|
||||
"name": "AP Chemistry",
|
||||
"teacherName": "Ms. Stone",
|
||||
"id": 33464,
|
||||
"assignmentsId": 10934148,
|
||||
"letterGrade": "A+",
|
||||
"numericGrade": 100.0,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "AP",
|
||||
"id_ci": 22,
|
||||
"rating": {
|
||||
"id_ci": 22,
|
||||
"id_user": 1,
|
||||
"userFullName": "Yijie]\u003d[Gui",
|
||||
"ratings": [
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
3,
|
||||
4
|
||||
],
|
||||
"comment": "Mr. Dankert can\u0027t really systematically teach, often he forgets to tell us something very important and then forgets that he forgot. The labs explained a lot of them very well."
|
||||
},
|
||||
"name": "AP Physics 1",
|
||||
"teacherName": "Mr. Dankert",
|
||||
"id": 32458,
|
||||
"assignmentsId": 10934143,
|
||||
"letterGrade": "A",
|
||||
"numericGrade": 95.67,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "AP",
|
||||
"id_ci": 18,
|
||||
"rating": {
|
||||
"id_ci": 18,
|
||||
"id_user": 1,
|
||||
"userFullName": "Yijie]\u003d[Gui",
|
||||
"ratings": [
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
5
|
||||
],
|
||||
"comment": "Psychology is the best, and most valuable class I\u0027ve ever taken! Everything is just so relatable to my life!"
|
||||
},
|
||||
"name": "AP Psychology",
|
||||
"teacherName": "Mr. Emerson",
|
||||
"id": 32451,
|
||||
"assignmentsId": 10934141,
|
||||
"letterGrade": "A+",
|
||||
"numericGrade": 100.0,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "A",
|
||||
"id_ci": 98,
|
||||
"rating": {
|
||||
"id_ci": 98,
|
||||
"id_user": 1,
|
||||
"userFullName": "Yijie]\u003d[Gui",
|
||||
"ratings": [
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
5
|
||||
],
|
||||
"comment": "Mr. Pynchon is very nice and encouraging."
|
||||
},
|
||||
"name": "US History A",
|
||||
"teacherName": "Mr. Pynchon",
|
||||
"id": 33093,
|
||||
"assignmentsId": 10941280,
|
||||
"letterGrade": "A+",
|
||||
"numericGrade": 99.39,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "H",
|
||||
"id_ci": 100,
|
||||
"name": "US History H",
|
||||
"teacherName": "Ms. Heath",
|
||||
"id": 33096,
|
||||
"assignmentsId": 10934144,
|
||||
"status": "past"
|
||||
},
|
||||
{
|
||||
"level": "A",
|
||||
"id_ci": 134,
|
||||
"name": "Relational Dynamics A",
|
||||
"teacherName": "Mr. Reinbold",
|
||||
"id": 33173,
|
||||
"assignmentsId": 10934146,
|
||||
"status": "past"
|
||||
},
|
||||
{
|
||||
"level": "A",
|
||||
"id_ci": 77,
|
||||
"rating": {
|
||||
"id_ci": 77,
|
||||
"id_user": -1,
|
||||
"userFullName": "Anonymous]\u003d[Student",
|
||||
"ratings": [
|
||||
5,
|
||||
4,
|
||||
5,
|
||||
5,
|
||||
3
|
||||
],
|
||||
"comment": "Honestly, everything is great except that the grading is way too harsh for an Accelerated course."
|
||||
},
|
||||
"name": "Social Justice A",
|
||||
"teacherName": "Mr. Reinbold",
|
||||
"id": 33121,
|
||||
"assignmentsId": 10934145,
|
||||
"letterGrade": "A+",
|
||||
"numericGrade": 97.0,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "Sport",
|
||||
"name": "Yoga AS",
|
||||
"teacherName": "Ms. Fanikos",
|
||||
"id": 33070,
|
||||
"assignmentsId": 10935189,
|
||||
"status": "past"
|
||||
},
|
||||
{
|
||||
"level": "Club",
|
||||
"id_ci": 316,
|
||||
"name": "HS Magic Trick Club 2019",
|
||||
"teacherName": "Mr. Reinbold",
|
||||
"id": 34174,
|
||||
"assignmentsId": 10949139,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "Club",
|
||||
"id_ci": 331,
|
||||
"name": "HS Science \u0026amp; Technology 2019",
|
||||
"teacherName": "Ms. Erwin",
|
||||
"id": 34197,
|
||||
"assignmentsId": 10952100,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "Club",
|
||||
"id_ci": 333,
|
||||
"name": "HS Computer Club 2019",
|
||||
"teacherName": "Mr. Gilmore",
|
||||
"id": 34199,
|
||||
"assignmentsId": 10951448,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"level": "Club",
|
||||
"id_ci": 336,
|
||||
"name": "HS Chinese Ambassadors Club 2019",
|
||||
"teacherName": "Mrs. Mills",
|
||||
"id": 34209,
|
||||
"assignmentsId": 10953979,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
@@ -1,5 +0,0 @@
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=1024"><link rel=icon href=/logo@32px.png><title>Veracross Analyzer</title><link href=/css/app.72ceade9.css rel=preload as=style><link href=/js/app.c7702e9a.js rel=preload as=script><link href=/js/chunk-vendors.4383782d.js rel=preload as=script><link href=/css/app.72ceade9.css rel=stylesheet></head><body style="margin: 0"><noscript><strong>We're sorry but veracross-analyzer doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js></script><script src=https://cdn.jsdelivr.net/npm/v-charts/lib/index.min.js></script><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/v-charts/lib/style.min.css><link rel=stylesheet href=https://unpkg.com/element-ui/lib/theme-chalk/index.css><link href="https://fonts.googleapis.com/css?family=Nunito+Sans&display=swap" rel=stylesheet><script src=/js/chunk-vendors.4383782d.js></script><script src=/js/app.c7702e9a.js></script></body><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>
|
||||
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Redirecting...</title>
|
||||
<meta http-equiv = "refresh" content = "0; url = https://vera.hydev.org/#info" />
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to (<a href="https://vera.hydev.org/#info">https://vera.hydev.org/#info</a>)...
|
||||
<script>
|
||||
window.location.href = 'https://vera.hydev.org/#info';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB |
Generated
+11605
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "veracross-analyzer",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^2.6.5",
|
||||
"echarts": "^4.2.1",
|
||||
"element-ui": "^2.11.1",
|
||||
"moment": "^2.24.0",
|
||||
"p-wait-for": "^3.1.0",
|
||||
"v-charts": "^1.19.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-class-component": "^7.0.2",
|
||||
"vue-cookies": "^1.5.13",
|
||||
"vue-property-decorator": "^8.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.10.0",
|
||||
"@vue/cli-plugin-typescript": "^3.10.0",
|
||||
"@vue/cli-service": "^3.10.0",
|
||||
"node-sass": "^4.9.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"typescript": "^3.4.3",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions"
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>Veracross Analyzer</title>
|
||||
</head>
|
||||
|
||||
<body style="margin: 0">
|
||||
<noscript>
|
||||
<strong>We're sorry but veracross-analyzer doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
|
||||
<div id="app"></div>
|
||||
|
||||
<!-- V-Charts -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/v-charts/lib/index.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/v-charts/lib/style.min.css">
|
||||
|
||||
<!-- ElementUI -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# abort on errors
|
||||
set -e
|
||||
|
||||
# build
|
||||
npm run build
|
||||
|
||||
# navigate into the build output directory
|
||||
cd dist
|
||||
|
||||
# if you are deploying to a custom domain
|
||||
echo 'vera.hydev.org' > CNAME
|
||||
|
||||
git init
|
||||
git add -A
|
||||
git commit -m 'deploy'
|
||||
|
||||
# if you are deploying to https://<USERNAME>.github.io/<REPO>
|
||||
git push -f git@github.com:HyDevelop/VeracrossAnalyzer.Client.git master:gh-pages
|
||||
|
||||
cd -
|
||||
@@ -0,0 +1,2 @@
|
||||
cd ../
|
||||
npm run serve
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,18 @@
|
||||
#app
|
||||
{
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.theme-default
|
||||
{
|
||||
--unread: #ff6c00;
|
||||
--main: #0c6dad;
|
||||
|
||||
--assignment-type-2: #3f991e;
|
||||
--assignment-type-3: #ff9900;
|
||||
--assignment-type-4: #b02b02;
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
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 Constants from '@/constants';
|
||||
import JsonUtils from '@/utils/json-utils';
|
||||
import pWaitFor from 'p-wait-for';
|
||||
import {HttpUtils} from '@/utils/http-utils';
|
||||
import {CourseUtils} from '@/utils/course-utils';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
|
||||
/**
|
||||
* Objects of this interface represent assignment grades.
|
||||
*/
|
||||
export interface Assignment
|
||||
{
|
||||
id: number,
|
||||
type: string,
|
||||
typeId: number,
|
||||
description: string,
|
||||
date: string,
|
||||
complete: string,
|
||||
include: boolean,
|
||||
display: boolean,
|
||||
|
||||
unread: boolean,
|
||||
|
||||
scoreMax: number,
|
||||
score: number
|
||||
}
|
||||
|
||||
/**
|
||||
* A course
|
||||
*/
|
||||
export interface Course
|
||||
{
|
||||
assignmentsId: number,
|
||||
id: number,
|
||||
name: string,
|
||||
teacherName: string,
|
||||
status: string,
|
||||
|
||||
letterGrade?: string,
|
||||
numericGrade?: number,
|
||||
|
||||
level: string,
|
||||
scaleUp: number,
|
||||
|
||||
grading:
|
||||
{
|
||||
method: string,
|
||||
weightingMap: {[index: string]: number}
|
||||
}
|
||||
|
||||
assignments: Assignment[]
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: {Login, Navigation, Overall},
|
||||
})
|
||||
export default class App extends Vue
|
||||
{
|
||||
// Is the login panel shown
|
||||
public showLogin: boolean = true;
|
||||
|
||||
// List of course that the student takes
|
||||
public courses: Course[] = [];
|
||||
|
||||
// List of course that should be displayed
|
||||
public filteredCourses: Course[] = [];
|
||||
|
||||
// Currently selected tab
|
||||
public selectedTab: string = 'overall';
|
||||
|
||||
// Are the course assignments loaded from the server.
|
||||
public assignmentsReady: boolean = false;
|
||||
|
||||
// Token
|
||||
public token: string = '';
|
||||
|
||||
// Http Client
|
||||
public http: HttpUtils = new HttpUtils('');
|
||||
|
||||
/**
|
||||
* This is called when the instance is created.
|
||||
*/
|
||||
public created()
|
||||
{
|
||||
// Show splash
|
||||
console.log(Constants.SPLASH);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the user logs in.
|
||||
*
|
||||
* @param token Authorization token
|
||||
*/
|
||||
public onLogin(token: string)
|
||||
{
|
||||
// Hide login bar
|
||||
this.showLogin = false;
|
||||
|
||||
// Store token
|
||||
this.token = token;
|
||||
|
||||
// Assign token to http client
|
||||
this.http.token = token;
|
||||
|
||||
// Load data
|
||||
this.loadCoursesAfterLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load courses data after login.
|
||||
*/
|
||||
public loadCoursesAfterLogin()
|
||||
{
|
||||
this.http.post('/courses', {}).then(response =>
|
||||
{
|
||||
// Check success
|
||||
if (response.success)
|
||||
{
|
||||
// Save courses
|
||||
this.courses = response.data;
|
||||
|
||||
// Load assignments
|
||||
this.loadAssignments();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show error message TODO: Show it properly
|
||||
alert(response.data);
|
||||
}
|
||||
})
|
||||
.catch(alert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the assignments of the courses
|
||||
*/
|
||||
public loadAssignments()
|
||||
{
|
||||
// Get assignments for all the courses
|
||||
this.courses.forEach(course =>
|
||||
{
|
||||
// Send request to get assignments
|
||||
this.http.post('/assignments', {id: course.assignmentsId}).then(response =>
|
||||
{
|
||||
// Check success
|
||||
if (response.success)
|
||||
{
|
||||
// Load assignments
|
||||
// Parse json and filter it
|
||||
course.assignments = JsonUtils.filterAssignments(response.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show error message TODO: Show it properly
|
||||
alert(response.data);
|
||||
}
|
||||
})
|
||||
.catch(alert);
|
||||
});
|
||||
|
||||
// Wait for assignments to be ready.
|
||||
pWaitFor(() => this.isAssignmentsReady()).then(() =>
|
||||
{
|
||||
// Filter courses
|
||||
this.filteredCourses = CourseUtils.getGradedCourses(this.courses);
|
||||
|
||||
// Check grading algorithms
|
||||
this.checkGradingAlgorithms();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
private checkGradingAlgorithms()
|
||||
{
|
||||
// Loop through all the courses
|
||||
for (const course of this.filteredCourses)
|
||||
{
|
||||
// Check if total-average grade is the same with percent-type grade
|
||||
if (course.numericGrade == +GPAUtils.getTotalMeanAverage(course).toFixed(2))
|
||||
{
|
||||
course.grading = {method: 'TOTAL_MEAN', weightingMap: {}};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Request grading scheme for this course
|
||||
this.http.post('/grading', {'assignment_id': course.assignmentsId}).then(response =>
|
||||
{
|
||||
// Check success
|
||||
if (response.success)
|
||||
{
|
||||
// Add it to course
|
||||
course.grading = response.data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show error message TODO: Show it properly
|
||||
alert(response.data)
|
||||
}
|
||||
})
|
||||
.catch(alert)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for done
|
||||
pWaitFor(() => this.isGradingReady()).then(() =>
|
||||
{
|
||||
// When the assignments are ready
|
||||
// TODO: Display loading
|
||||
this.assignmentsReady = true;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Are grading algorithms ready or not.
|
||||
*
|
||||
* @returns boolean Ready or not
|
||||
*/
|
||||
private isGradingReady(): boolean
|
||||
{
|
||||
for (const course of this.filteredCourses)
|
||||
{
|
||||
if (course.grading == undefined)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a navigation tab is clicked
|
||||
*
|
||||
* @param tab Tab name
|
||||
*/
|
||||
public onNavigate(tab: string)
|
||||
{
|
||||
// Debug output TODO: Remove this
|
||||
console.log(tab);
|
||||
|
||||
// Update selected tab
|
||||
this.selectedTab = tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign out
|
||||
*/
|
||||
public signOut()
|
||||
{
|
||||
// Clear all cookies
|
||||
this.$cookies.keys().forEach(key => this.$cookies.remove(key));
|
||||
|
||||
// Refresh
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div id="app" class="theme-default">
|
||||
<login v-if="showLogin" v-on:login:token="onLogin" :http="http"></login>
|
||||
<navigation :courses="filteredCourses"
|
||||
v-on:sign-out="signOut()"
|
||||
v-on:navigation:select="onNavigate">
|
||||
</navigation>
|
||||
|
||||
<div id="app-content">
|
||||
<overall :courses="filteredCourses"
|
||||
v-if="selectedTab === 'overall' && assignmentsReady">
|
||||
</overall>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./app.ts" lang="ts"></script>
|
||||
<style src="./app.scss" lang="scss"></style>
|
||||
@@ -0,0 +1,99 @@
|
||||
|
||||
// Parent div for login
|
||||
#login
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Parent overlay
|
||||
.login-overlay
|
||||
{
|
||||
// Credit to w3schools.com:
|
||||
// https://www.w3schools.com/howto/howto_js_fullscreen_overlay.asp
|
||||
|
||||
// Fill entire screen
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
// Stay in place
|
||||
position: fixed;
|
||||
|
||||
// Sit on top layer
|
||||
z-index: 1;
|
||||
|
||||
// Overlay color
|
||||
background-color: rgba(0,0,0, 0.65);
|
||||
|
||||
// Disable horizontal scroll
|
||||
overflow-x: hidden;
|
||||
|
||||
// Make it a table for vertical centering
|
||||
display: table;
|
||||
}
|
||||
|
||||
.login-vertical-center
|
||||
{
|
||||
// Vertically center
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// The user interacting panel
|
||||
.login-panel
|
||||
{
|
||||
// Make it smaller
|
||||
width: 256px;
|
||||
|
||||
// Center
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
// Borders
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
// box-shadow: 0 0 20px 0 white;
|
||||
border: 1px solid #DCDFE6;
|
||||
|
||||
// Make it white
|
||||
background-color: white;
|
||||
|
||||
// Input bars
|
||||
.el-input
|
||||
{
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
// Button
|
||||
.el-button
|
||||
{
|
||||
margin: 5px 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Error
|
||||
.input-error
|
||||
{
|
||||
.el-input__inner
|
||||
{
|
||||
color: #ff3a3a6b !important;
|
||||
border-color: #ff3a3a6b !important;
|
||||
background-color: #ffdddd3b !important;
|
||||
}
|
||||
|
||||
.el-input__inner:focus
|
||||
{
|
||||
background-color: white !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix error message
|
||||
.el-form-item__error.custom
|
||||
{
|
||||
padding-top: 0;
|
||||
position: relative;
|
||||
top: auto;
|
||||
float: left;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import Constants from '@/constants';
|
||||
import {HttpUtils} from '@/utils/http-utils';
|
||||
|
||||
/**
|
||||
* This component handles user login, and obtains data from the server.
|
||||
* TODO: Press enter to login
|
||||
*/
|
||||
@Component({
|
||||
components: {},
|
||||
})
|
||||
export default class Login extends Vue
|
||||
{
|
||||
public username: any = '';
|
||||
public password: any = '';
|
||||
|
||||
public loading: boolean = false;
|
||||
public error: String = '';
|
||||
|
||||
@Prop()
|
||||
public http?: HttpUtils;
|
||||
|
||||
/**
|
||||
* This is called when the instance is created.
|
||||
*/
|
||||
public created()
|
||||
{
|
||||
// Check login cookies
|
||||
if (this.$cookies.isKey('va.token'))
|
||||
{
|
||||
// Already contains valid token / TODO: Validate
|
||||
this.$emit('login:token', this.$cookies.get('va.token'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On click, sends username and password to the server.
|
||||
*/
|
||||
public onLoginClick()
|
||||
{
|
||||
// Make login button loading
|
||||
this.loading = true;
|
||||
|
||||
// Fetch request
|
||||
(<HttpUtils> this.http).post('/login', {username: this.username, password: this.password})
|
||||
.then(response =>
|
||||
{
|
||||
// Check success
|
||||
if (response.success)
|
||||
{
|
||||
// Save token to cookies
|
||||
this.$cookies.set('va.token', response.data, '7d');
|
||||
|
||||
// Call custom event with token
|
||||
this.$emit('login:token', response.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show error message
|
||||
this.error = response.data;
|
||||
|
||||
// Allow the user to retry
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch(err =>
|
||||
{
|
||||
alert(err);
|
||||
|
||||
// Allow the user to retry
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the user hits enter in the input boxes.
|
||||
*/
|
||||
public onEnter()
|
||||
{
|
||||
this.onLoginClick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div id="login" class="login-overlay">
|
||||
<div class="login-vertical-center">
|
||||
<div class="login-panel">
|
||||
<img alt="Vue logo" src="../../assets/logo.png">
|
||||
|
||||
<h1>Veracross Analyzer</h1>
|
||||
|
||||
<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>
|
||||
|
||||
<div class="el-form-item__error custom">{{error}}</div>
|
||||
|
||||
<el-button plain type="primary" @click="onLoginClick" :loading="loading">Login</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./login.ts" lang="ts"></script>
|
||||
<style src="./login.scss" lang="scss"></style>
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
.el-menu.centered li
|
||||
{
|
||||
display: inline-block !important;
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
// Borders
|
||||
#navigation
|
||||
{
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||
|
||||
ul
|
||||
{
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#sign-out-button
|
||||
{
|
||||
// Float right
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
// Set width and height
|
||||
height: 60px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
#nav-title
|
||||
{
|
||||
// Float left
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
||||
// Set height
|
||||
height: 60px;
|
||||
|
||||
// Center text
|
||||
align-items: center;
|
||||
|
||||
// Margins
|
||||
margin-left: 14px;
|
||||
margin-right: 8px;
|
||||
|
||||
// Font
|
||||
font-size: 1.25rem;
|
||||
display: inline-flex;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import Constants from '@/constants';
|
||||
|
||||
/**
|
||||
* This component is the top navigation bar
|
||||
*/
|
||||
@Component({
|
||||
components: {},
|
||||
})
|
||||
export default class Navigation extends Vue
|
||||
{
|
||||
public activeIndex: string = 'overall';
|
||||
|
||||
@Prop() courses: any;
|
||||
|
||||
/**
|
||||
* This function is called when the selection changes.
|
||||
*
|
||||
* @param index The index selected
|
||||
* @param indexPath The path of the index
|
||||
*/
|
||||
public onSelect(index: string, indexPath: string)
|
||||
{
|
||||
// Update active index
|
||||
this.activeIndex = index;
|
||||
|
||||
// Call custom event
|
||||
this.$emit('navigation:select', this.activeIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called when the sign out button is clicked.
|
||||
*/
|
||||
public signOut()
|
||||
{
|
||||
// Call custom event
|
||||
this.$emit('sign-out');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div id="navigation">
|
||||
<el-menu style="margin-bottom: 10px;"
|
||||
class="centered" :default-active="activeIndex" mode="horizontal" @select="onSelect">
|
||||
|
||||
<!--div id="nav-title">
|
||||
Veracross Analyzer
|
||||
</div-->
|
||||
|
||||
<el-menu-item index="overall">Overall</el-menu-item>
|
||||
|
||||
<el-submenu index="courses">
|
||||
<template slot="title">Courses</template>
|
||||
<el-menu-item v-for="course in courses"
|
||||
:index="`course-${course.name}`"
|
||||
:key="course.name">{{course.name}}</el-menu-item>
|
||||
</el-submenu>
|
||||
|
||||
<el-button @click="signOut" id="sign-out-button" type="text">Sign Out</el-button>
|
||||
</el-menu>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./navigation.ts" lang="ts"></script>
|
||||
<style src="./navigation.scss" lang="scss"></style>
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* This class stores the static constants.
|
||||
*/
|
||||
export default class Constants
|
||||
{
|
||||
/**
|
||||
* Base url for api access
|
||||
*/
|
||||
public static API_URL: string = 'https://va.hydev.org/api';
|
||||
|
||||
public static VERSION: string = '0.3.2.452';
|
||||
|
||||
public static GITHUB: string = 'https://github.com/HyDevelop/VeracrossAnalyzer.Client';
|
||||
|
||||
public static SPLASH: string =
|
||||
'. , ,---. | \n' +
|
||||
'| |. , |---|,---.,---.| , .,---,,---.,---.\n' +
|
||||
' \\ / >< | || |,---|| | | .-\' |---\'| \n' +
|
||||
' `\' \' ` ` \'` \'`---^`---\'`---|\'---\'`---\'` \n' +
|
||||
' `---\' \n' +
|
||||
` Version v${Constants.VERSION} by Hykilpikonna (YGui21)\n` +
|
||||
` Github: ${Constants.GITHUB}`;
|
||||
|
||||
// Graph Theme
|
||||
public static THEME =
|
||||
{
|
||||
// Colors
|
||||
colors:
|
||||
[
|
||||
'#18cea5',
|
||||
'#4fa8ed',
|
||||
'#f9627b',
|
||||
'#ffb075',
|
||||
'#005c9c',
|
||||
'#bcabe0',
|
||||
'#d36e75',
|
||||
'#fc97af',
|
||||
'#919e8b',
|
||||
'#d7ab82',
|
||||
'#6e7074',
|
||||
'#61a0a8',
|
||||
'#efa18d',
|
||||
'#787464',
|
||||
'#cc7e63',
|
||||
'#724e58',
|
||||
'#4b565b'
|
||||
]
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
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';
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
// Use Element UI
|
||||
Vue.use(ElementUI, {locale: 'en-us'});
|
||||
|
||||
// Use VCharts
|
||||
Vue.use(VCharts);
|
||||
|
||||
// Use Cookies
|
||||
Vue.use(VueCookies);
|
||||
|
||||
// Init app
|
||||
new Vue({
|
||||
render: (h) => h(App),
|
||||
}).$mount('#app');
|
||||
@@ -0,0 +1,7 @@
|
||||
#overall-bar
|
||||
{
|
||||
.graph
|
||||
{
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Course} from '@/components/app/app';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
import Constants from '@/constants';
|
||||
|
||||
@Component({
|
||||
})
|
||||
export default class OverallBar extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) courses: Course[];
|
||||
|
||||
/**
|
||||
* Generate settings
|
||||
*/
|
||||
get chartSettings()
|
||||
{
|
||||
let settings =
|
||||
{
|
||||
// Title
|
||||
title:
|
||||
{
|
||||
show: true,
|
||||
textStyle:
|
||||
{
|
||||
fontSize: 12
|
||||
},
|
||||
text: 'Course GPA',
|
||||
subtext: 'Current GPA for every course',
|
||||
x: 'center'
|
||||
},
|
||||
|
||||
// X axis represents course names
|
||||
xAxis:
|
||||
{
|
||||
type: 'category',
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
inside: false,
|
||||
rotate: 90,
|
||||
|
||||
// Truncate text length
|
||||
formatter: (value: string) => value.length <= 16 ? value : value.substr(0, 14) + '...'
|
||||
},
|
||||
},
|
||||
|
||||
// Y axis represents GPAs and MaxGPAs
|
||||
yAxis:
|
||||
{
|
||||
type: 'value'
|
||||
},
|
||||
|
||||
// Data
|
||||
series:
|
||||
[
|
||||
{
|
||||
type: 'bar',
|
||||
barGap: '-100%',
|
||||
data: this.courses.map(course =>
|
||||
{
|
||||
return {value: [course.name, GPAUtils.getGP(course, 'A+')],
|
||||
itemStyle: {color: '#d8d8d8'}}
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barGap: '-100%',
|
||||
data: this.generateGPData(),
|
||||
|
||||
label:
|
||||
{
|
||||
show: true,
|
||||
rotate: 90
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
// Disable tooltip
|
||||
tooltip:
|
||||
{
|
||||
show: false
|
||||
}
|
||||
};
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate GP data for each course
|
||||
*/
|
||||
private generateGPData()
|
||||
{
|
||||
let data: any = [];
|
||||
|
||||
this.courses.forEach(course =>
|
||||
{
|
||||
data.push(
|
||||
{
|
||||
value: [course.name, GPAUtils.getGP(course, course.letterGrade)],
|
||||
itemStyle:
|
||||
{
|
||||
color: Constants.THEME.colors[data.length]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div id="overall-bar">
|
||||
<ve-bar height="450px" class="graph"
|
||||
:extend="chartSettings"></ve-bar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./overall-bar.ts" lang="ts"></script>
|
||||
<style src="./overall-bar.scss" lang="scss"></style>
|
||||
@@ -0,0 +1,132 @@
|
||||
// Card
|
||||
.el-card.course-card
|
||||
{
|
||||
// Margins
|
||||
margin-right: 20px;
|
||||
margin-left: 20px;
|
||||
|
||||
// Height limit
|
||||
max-height: 250px;
|
||||
|
||||
// Limit name length
|
||||
white-space: nowrap;
|
||||
|
||||
// Expansion color
|
||||
background: #f9f9f9;
|
||||
|
||||
// Main card content
|
||||
.course-card-content.main
|
||||
{
|
||||
padding: 0 20px 0 20px;
|
||||
height: 90px;
|
||||
|
||||
// Main color
|
||||
background: white;
|
||||
}
|
||||
|
||||
// Expansion content
|
||||
.course-card-content.expand.notification
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Remove card padding for styling issues
|
||||
div.el-card.course-card > div.el-card__body
|
||||
{
|
||||
padding-right: 0 !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.course-col-name
|
||||
{
|
||||
// Align left
|
||||
text-align: left;
|
||||
float: left;
|
||||
|
||||
.course-name
|
||||
{
|
||||
overflow: hidden;
|
||||
font-size: 22px;
|
||||
color: var(--main);
|
||||
}
|
||||
|
||||
.course-name:hover
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-card__body
|
||||
{
|
||||
margin: auto 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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 UnreadEntry from '@/pages/overall/overall-course/unread-entry/unread-entry';
|
||||
|
||||
@Component({
|
||||
components: {UnreadEntry}
|
||||
})
|
||||
export default class OverallCourse 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;
|
||||
}
|
||||
|
||||
unreadTable()
|
||||
{
|
||||
return [{date: '2019-10-04', name: 'Letter from a Penn Farmer Discussion Forum', grade: '14|14'}];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div id="overall-course">
|
||||
<el-card class="course-card">
|
||||
<div class="course-card-content main vertical-center">
|
||||
<el-row>
|
||||
<el-col :span="6" class="course-col-name">
|
||||
<div class="course-name">
|
||||
{{course.name}}
|
||||
</div>
|
||||
<div class="course-teacher">
|
||||
{{course.teacherName}}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
|
||||
</el-col>
|
||||
<el-col :span="6" class="course-col-grade">
|
||||
<div class="course-grade">
|
||||
<span class="letter">{{course.letterGrade}} </span>
|
||||
<span class="numeric">{{course.numericGrade.toFixed(2)}}</span>
|
||||
<span class="percent">%</span>
|
||||
</div>
|
||||
<div class="course-updates" :class="countUnread() === 0 ? 'none' : 'unread'">
|
||||
<span class="unread-number">
|
||||
{{countUnread()}}
|
||||
</span>
|
||||
<span class="unread-text">
|
||||
new update{{countUnread() >= 2 ? 's' : ''}}
|
||||
</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="course-card-content expand notification"
|
||||
v-if="countUnread() !== 0">
|
||||
<unread-entry v-for="assignment in unreadAssignments"
|
||||
:assignment="assignment"
|
||||
:key="assignment.id">
|
||||
</unread-entry>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./overall-course.ts" lang="ts"></script>
|
||||
<style src="./overall-course.scss" lang="scss"></style>
|
||||
@@ -0,0 +1,93 @@
|
||||
|
||||
// Row
|
||||
.unread-entry
|
||||
{
|
||||
height: 40px;
|
||||
padding: 0 20px 0 20px;
|
||||
background: #f5f7fa;
|
||||
|
||||
text-align: left;
|
||||
|
||||
// Date
|
||||
.el-col.date
|
||||
{
|
||||
span.month
|
||||
{
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
span.now
|
||||
{
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
|
||||
.el-col.description
|
||||
{
|
||||
span.type
|
||||
{
|
||||
display: inline-block;
|
||||
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
|
||||
background: #eee;
|
||||
border-left: 2px solid #000;
|
||||
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// Grade
|
||||
.el-col.grade
|
||||
{
|
||||
text-align: right;
|
||||
|
||||
span.percent
|
||||
{
|
||||
font-style: italic;
|
||||
background: #ffc;
|
||||
color: #555;
|
||||
|
||||
margin-right: 8px;
|
||||
|
||||
.symbol
|
||||
{
|
||||
padding: 0 1px;
|
||||
}
|
||||
}
|
||||
|
||||
// Score you got
|
||||
span.score
|
||||
{
|
||||
background: #f2f2f2;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
// Max score
|
||||
span.max
|
||||
{
|
||||
background: #ddd;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.entry-box
|
||||
{
|
||||
height: 22px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.unread-entry:first-child
|
||||
{
|
||||
padding-top: 3px;
|
||||
|
||||
// Top shadow
|
||||
// https://stackoverflow.com/questions/17572619/inset-box-shadow-only-on-one-side
|
||||
box-shadow: inset 0 7px 9px -7px rgba(0,0,0,0.1);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
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
|
||||
*/
|
||||
private getMoment(date: string)
|
||||
{
|
||||
return moment(new Date(date));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="unread-entry vertical-center">
|
||||
<el-row class="unread-row">
|
||||
<el-col :span="3" class="date">
|
||||
<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})`">
|
||||
{{assignment.type}}
|
||||
</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)}}
|
||||
<span class="symbol">%</span>
|
||||
</span>
|
||||
<span class="score entry-box">{{assignment.score}}</span>
|
||||
<span class="max entry-box">{{assignment.scoreMax}}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./unread-entry.ts" lang="ts"></script>
|
||||
<style src="./unread-entry.scss" lang="scss"></style>
|
||||
@@ -0,0 +1,179 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Course} from '@/components/app/app';
|
||||
|
||||
@Component({
|
||||
})
|
||||
export default class OverallLine extends Vue
|
||||
{
|
||||
// @ts-ignore
|
||||
@Prop({required: true}) courses: Course[];
|
||||
|
||||
private settings =
|
||||
{
|
||||
// Title
|
||||
title:
|
||||
{
|
||||
show: true,
|
||||
textStyle:
|
||||
{
|
||||
fontSize: 12
|
||||
},
|
||||
text: 'Average Grade',
|
||||
subtext: 'Average score trend for every course',
|
||||
x: 'center'
|
||||
},
|
||||
// Legend
|
||||
legend:
|
||||
{
|
||||
show: false,
|
||||
//left: 'auto',
|
||||
//align: 'left',
|
||||
//orient: 'vertical'
|
||||
textStyle:
|
||||
{
|
||||
fontSize: 11
|
||||
},
|
||||
icon: 'circle'
|
||||
},
|
||||
// Zoom bar
|
||||
dataZoom:
|
||||
[
|
||||
// TODO: Calculate real value for startValue
|
||||
{
|
||||
startValue: '9/13/2019'
|
||||
},
|
||||
{
|
||||
type: 'inside'
|
||||
}
|
||||
],
|
||||
series:
|
||||
{
|
||||
smooth: true
|
||||
},
|
||||
xAxis:
|
||||
{
|
||||
//type: 'time'
|
||||
},
|
||||
yAxis:
|
||||
{
|
||||
min: (value: any) => value.min - 10
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert assignments list to a graph dataset.
|
||||
*/
|
||||
get convertChart()
|
||||
{
|
||||
let courses = this.courses;
|
||||
|
||||
// Compute the column names
|
||||
let columns = courses.map(course => course.name);
|
||||
columns.unshift('date');
|
||||
|
||||
// Find the min date
|
||||
let minDates = courses.map(course => new Date(course.assignments[course.assignments.length - 1].date).getTime());
|
||||
let minDate: Date = new Date(Math.min.apply(null, minDates));
|
||||
|
||||
// Find the dates in between
|
||||
let now = new Date();
|
||||
let dates = [];
|
||||
for (let date = minDate; date <= now; date.setDate(date.getDate() + 1))
|
||||
{
|
||||
dates.push(new Date(date));
|
||||
}
|
||||
|
||||
// Compute the rows data
|
||||
let rows: {[index: string]: any}[] = [];
|
||||
dates.forEach(date =>
|
||||
{
|
||||
// Define row object
|
||||
let row: {[index: string]:any} = {'date': date.toLocaleDateString('en-US')};
|
||||
|
||||
// Loop through courses
|
||||
courses.forEach(course =>
|
||||
{
|
||||
// Total Mean
|
||||
if (course.grading.method == 'TOTAL_MEAN')
|
||||
{
|
||||
let score = 0;
|
||||
let max = 0;
|
||||
|
||||
// Loop through assignments
|
||||
course.assignments.forEach(assignment =>
|
||||
{
|
||||
// If assignment should be displayed
|
||||
if (assignment.complete != 'Complete') return;
|
||||
|
||||
// Date is being looked at
|
||||
let assignmentDate = new Date(assignment.date);
|
||||
if (assignmentDate.getTime() < date.getTime())
|
||||
{
|
||||
// Record scores
|
||||
score += assignment.score;
|
||||
max += assignment.scoreMax;
|
||||
}
|
||||
});
|
||||
|
||||
// Add average to the row
|
||||
row[course.name] = score / max * 100;
|
||||
}
|
||||
else if (course.grading.method == 'PERCENT_TYPE')
|
||||
{
|
||||
let typeScores: {[index: string]: any} = {};
|
||||
let typeCounts: {[index: string]: any} = {};
|
||||
|
||||
// Loop through assignments
|
||||
course.assignments.forEach(assignment =>
|
||||
{
|
||||
// If assignment should be displayed
|
||||
if (assignment.complete != 'Complete') return;
|
||||
|
||||
// Date is being looked at
|
||||
let assignmentDate = new Date(assignment.date);
|
||||
if (assignmentDate.getTime() < date.getTime())
|
||||
{
|
||||
// Record scores
|
||||
if (typeScores[assignment.type] == undefined) typeScores[assignment.type] = 0;
|
||||
typeScores[assignment.type] += assignment.score / assignment.scoreMax;
|
||||
|
||||
if (typeCounts[assignment.type] == undefined) typeCounts[assignment.type] = 0;
|
||||
typeCounts[assignment.type] ++;
|
||||
}
|
||||
});
|
||||
|
||||
// Count total percentage (This is to avoid less than expected cases)
|
||||
// Eg. If HW = 25% and Quiz = 75%, I have 1 hw and 0 quiz
|
||||
// Without total percentage, the avg grade I get is 25%.
|
||||
let totalPercentage = 0;
|
||||
for (let type in course.grading.weightingMap)
|
||||
{
|
||||
if (typeScores[type] != undefined)
|
||||
{
|
||||
totalPercentage += course.grading.weightingMap[type];
|
||||
}
|
||||
}
|
||||
|
||||
// Count
|
||||
let score = 0;
|
||||
for (let type in typeScores)
|
||||
{
|
||||
let typeFactor = course.grading.weightingMap[type] / totalPercentage;
|
||||
score += typeScores[type] * typeFactor / typeCounts[type];
|
||||
}
|
||||
|
||||
// Add average to the row
|
||||
if (score != 0) row[course.name] = score * 100;
|
||||
}
|
||||
});
|
||||
|
||||
// Add it to the array
|
||||
rows.push(row);
|
||||
});
|
||||
|
||||
return {
|
||||
columns: columns,
|
||||
rows: rows
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div id="overall-line">
|
||||
<ve-line :data="convertChart" :extend="settings"></ve-line>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./overall-line.ts" lang="ts"></script>
|
||||
<style src="./overall-line.scss" lang="scss"></style>
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
// Add some margins
|
||||
.el-card
|
||||
{
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-card.large
|
||||
{
|
||||
height: 494px;
|
||||
}
|
||||
|
||||
.gpa-card
|
||||
{
|
||||
margin-left: 20px;
|
||||
min-width: 136px;
|
||||
}
|
||||
|
||||
.gpa
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.gpa.header
|
||||
{
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.gpa.text
|
||||
{
|
||||
font-size: 35px;
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.gpa.max
|
||||
{
|
||||
margin-top: -10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.gpa.time
|
||||
{
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
// Cards
|
||||
.el-card.overall-bar-card
|
||||
{
|
||||
margin-right: 20px;
|
||||
min-width: 170px;
|
||||
}
|
||||
|
||||
// Fix padding
|
||||
.el-card__body
|
||||
{
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
// Vertical centering
|
||||
.vertical-center
|
||||
{
|
||||
// Vertical center
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div id="overall">
|
||||
<el-row>
|
||||
<el-col :span="4">
|
||||
<el-card class="large gpa-card vertical-center">
|
||||
<div style="padding: 14px;">
|
||||
<span class="gpa header">GPA:</span>
|
||||
<span class="gpa text">{{getGPA().gpa}}</span>
|
||||
<span class="gpa max">(Out of {{getGPA().max}})</span>
|
||||
<div class="bottom clearfix gpa time">
|
||||
<time>{{ new Date().toDateString() }}</time>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-card class="large overall-line-card vertical-center">
|
||||
<overall-line :courses="courses"></overall-line>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="large overall-bar-card vertical-center">
|
||||
<overall-bar :courses="courses"></overall-bar>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<overall-course v-for="course in courses"
|
||||
:course="course"
|
||||
:key="course.id">
|
||||
</overall-course>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./overall.ts" lang="ts"></script>
|
||||
<style src="./overall.scss" lang="scss"></style>
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import Vue, { VNode } from 'vue';
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
// tslint:disable no-empty-interface
|
||||
interface Element extends VNode {}
|
||||
// tslint:disable no-empty-interface
|
||||
interface ElementClass extends Vue {}
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue';
|
||||
export default Vue;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import {Course} from '@/components/app/app';
|
||||
|
||||
export class CourseUtils
|
||||
{
|
||||
/**
|
||||
* Return a list of courses that are graphed
|
||||
*
|
||||
* @param original Original course list
|
||||
* @return Course[] Filtered course list
|
||||
*/
|
||||
public static getGradedCourses(original: Course[]): Course[]
|
||||
{
|
||||
// Define result
|
||||
let result: Course[] = [];
|
||||
|
||||
// Filter through courses
|
||||
original.forEach(course =>
|
||||
{
|
||||
// Skip future or past courses
|
||||
if (course.status != 'active') return;
|
||||
|
||||
// Skip courses without levels
|
||||
if (course.level == 'None') return;
|
||||
|
||||
// Skip courses without graded assignments
|
||||
if (course.assignments.filter(a => a.complete == 'Complete').length == 0) return;
|
||||
|
||||
// Skip if there are no grading scale
|
||||
// if (course.grading.method == 'NOT_GRADED') return;
|
||||
|
||||
// Add it to the list
|
||||
result.push(course);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Initialize course specific variables
|
||||
let courseScores: {[index: string]: any} = {};
|
||||
let courseMaxScores: {[index: string]: any} = {};
|
||||
let courseIndexes: {[index: string]: any} = {};
|
||||
courses.forEach(course =>
|
||||
{
|
||||
courseScores[course.name] = 0;
|
||||
courseMaxScores[course.name] = 0;
|
||||
courseIndexes[course.name] = course.assignments.length - 1;
|
||||
});
|
||||
|
||||
// Compute the rows data
|
||||
let rows: {[index: string]: any}[] = [];
|
||||
dates.forEach(date =>
|
||||
{
|
||||
// Define row object
|
||||
let row: {[index: string]:any} = {'date': date.toLocaleDateString('en-US')};
|
||||
|
||||
// Loop through courses
|
||||
courses.forEach(course =>
|
||||
{
|
||||
// Reversed loop through the assignments
|
||||
for (let r = courseIndexes[course.name]; r >= 0; r--)
|
||||
{
|
||||
let assignment = course.assignments[r];
|
||||
|
||||
// If assignment should be displayed
|
||||
if (assignment.complete != 'Complete') continue;
|
||||
|
||||
// Date is being looked at
|
||||
let assignmentDate = new Date(assignment.date);
|
||||
if (assignmentDate.getTime() == date.getTime())
|
||||
{
|
||||
// Detect grading method and record scores
|
||||
if (course.grading.method == 'TOTAL_MEAN')
|
||||
{
|
||||
courseScores[course.name] += assignment.score;
|
||||
courseMaxScores[course.name] += assignment.scoreMax;
|
||||
}
|
||||
else if (course.grading.method == 'PERCENT_TYPE')
|
||||
{
|
||||
let scale = course.grading.weightingMap[assignment.type];
|
||||
courseScores[course.name] += assignment.score * scale;
|
||||
courseMaxScores[course.name] += assignment.scoreMax * scale;
|
||||
}
|
||||
}
|
||||
|
||||
// Not now
|
||||
else if (assignmentDate > date)
|
||||
{
|
||||
courseIndexes[course.name] = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add average to the row
|
||||
row[course.name] = courseScores[course.name] / courseMaxScores[course.name] * 100;
|
||||
});
|
||||
|
||||
// Add it to the array
|
||||
rows.push(row);
|
||||
});
|
||||
|
||||
|
||||
else if (course.grading.method == 'PERCENT_TYPE')
|
||||
{
|
||||
let typeScores: {[index: string]: any} = {};
|
||||
let typeCounts: {[index: string]: any} = {};
|
||||
|
||||
// Loop through assignments
|
||||
course.assignments.forEach(assignment =>
|
||||
{
|
||||
// If assignment should be displayed
|
||||
if (assignment.complete != 'Complete') return;
|
||||
|
||||
// Date is being looked at
|
||||
let assignmentDate = new Date(assignment.date);
|
||||
if (assignmentDate.getTime() < date.getTime())
|
||||
{
|
||||
// Record scores
|
||||
if (typeScores[assignment.type] == undefined) typeScores[assignment.type] = 0;
|
||||
typeScores[assignment.type] += assignment.score / assignment.scoreMax;
|
||||
|
||||
if (typeCounts[assignment.type] == undefined) typeCounts[assignment.type] = 0;
|
||||
typeCounts[assignment.type] ++;
|
||||
}
|
||||
});
|
||||
|
||||
let score = 0;
|
||||
|
||||
// Count
|
||||
for (let type in typeScores)
|
||||
{
|
||||
score += typeScores[type] * course.grading.weightingMap[type] / typeCounts[type];
|
||||
console.log(type);
|
||||
}
|
||||
|
||||
// Add average to the row
|
||||
row[course.name] = score * 100;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* This is an utility class to calculate GPA.
|
||||
*/
|
||||
import {Course} from '@/components/app/app';
|
||||
|
||||
export class GPAUtils
|
||||
{
|
||||
// [[Min score, Letter grade, Base GPA], ...]
|
||||
public static SCALE =
|
||||
[
|
||||
[96.5, 'A+', 4.00],
|
||||
[92.5, 'A' , 3.75],
|
||||
[89.5, 'A-', 3.50],
|
||||
[86.5, 'B+', 3.25],
|
||||
[82.5, 'B' , 3.00],
|
||||
[79.5, 'B-', 2.75],
|
||||
[76.5, 'C+', 2.50],
|
||||
[72.5, 'C' , 2.25],
|
||||
[70.5, 'C-', 2.00],
|
||||
[69.5, 'D' , 1.00],
|
||||
[0 , 'F' , 0.00]
|
||||
];
|
||||
|
||||
// Keywords
|
||||
public static MIN = 0;
|
||||
public static LETTER = 1;
|
||||
public static GPA = 2;
|
||||
|
||||
/**
|
||||
* Calculate GPA for a list of couses
|
||||
*
|
||||
* @param coursesOriginal List of courses
|
||||
*/
|
||||
public static getGPA(coursesOriginal: Course[]): {gpa: number, accurate: boolean, max: number}
|
||||
{
|
||||
// Clone array
|
||||
let courses: Course[] = [];
|
||||
|
||||
// Accurate or not
|
||||
let accurate: boolean = true;
|
||||
|
||||
// Remove all courses that does not have a grade
|
||||
coursesOriginal.forEach(course =>
|
||||
{
|
||||
if (course.letterGrade == null || course.letterGrade == '')
|
||||
{
|
||||
accurate = false;
|
||||
}
|
||||
else if (course.level != 'none')
|
||||
{
|
||||
courses.push(course);
|
||||
}
|
||||
});
|
||||
|
||||
// If no course have grade, return -1
|
||||
if (courses.length == 0)
|
||||
{
|
||||
return {gpa: -1, accurate: false, max: -1};
|
||||
}
|
||||
|
||||
// Count total GPA
|
||||
let totalGPA = 0;
|
||||
let maxTotal = 0;
|
||||
courses.forEach(course =>
|
||||
{
|
||||
totalGPA += this.getGP(course, course.letterGrade);
|
||||
maxTotal += this.getGP(course, 'A+');
|
||||
});
|
||||
|
||||
// Get average GPA, round to two decimal places
|
||||
let gpa = Math.round(totalGPA / courses.length * 100) / 100;
|
||||
let maxGPA = Math.round(maxTotal / courses.length * 100) / 100;
|
||||
|
||||
// Return results
|
||||
return {gpa: gpa, accurate: accurate, max: maxGPA};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate GPA for a course
|
||||
*
|
||||
* @param course Course
|
||||
* @param letterGrade Letter grade
|
||||
*/
|
||||
public static getGP(course: Course, letterGrade?: string): number
|
||||
{
|
||||
// Find the GPA for this course.
|
||||
for (let scale of this.SCALE)
|
||||
{
|
||||
// Letter grades are the same
|
||||
if (scale[this.LETTER] == letterGrade)
|
||||
{
|
||||
// Get grade and add it
|
||||
let grade = <number> scale[this.GPA];
|
||||
|
||||
// Add scaleUp if not failed.
|
||||
if (grade != 0) grade += course.scaleUp;
|
||||
|
||||
// That's it
|
||||
return grade;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the total-mean (total/max) average
|
||||
*
|
||||
* @param course Course
|
||||
*/
|
||||
public static getTotalMeanAverage(course: Course)
|
||||
{
|
||||
let score = 0;
|
||||
let max = 0;
|
||||
|
||||
// Loop through assignments
|
||||
course.assignments.forEach(assignment =>
|
||||
{
|
||||
// If assignment should be displayed
|
||||
if (assignment.complete != 'Complete') return;
|
||||
|
||||
// Record scores
|
||||
score += assignment.score;
|
||||
max += assignment.scoreMax;
|
||||
});
|
||||
|
||||
// Return
|
||||
return score / max * 100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Constants from '@/constants';
|
||||
|
||||
export class HttpUtils
|
||||
{
|
||||
public token: string = '';
|
||||
|
||||
constructor (token: string)
|
||||
{
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public post(node: string, body: any): Promise<any>
|
||||
{
|
||||
// Add token
|
||||
if (this.token != '') body['token'] = this.token;
|
||||
|
||||
// Create promise
|
||||
return new Promise<any>((resolve, reject) =>
|
||||
{
|
||||
// Fetch request
|
||||
fetch(`${Constants.API_URL}${node}`, {method: 'POST', body: JSON.stringify(body)}).then(res =>
|
||||
{
|
||||
// Get response body text
|
||||
res.text().then(text =>
|
||||
{
|
||||
// Parse response
|
||||
let response = JSON.parse(text);
|
||||
resolve(response);
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
.catch(reject)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import {Assignment} from '@/components/app/app';
|
||||
|
||||
export default class JsonUtils
|
||||
{
|
||||
/**
|
||||
* This method filters the information provided in an assignments json.
|
||||
*
|
||||
* @param assignments Assignments object
|
||||
* @returns Assignment[] Filtered assignment grade object list
|
||||
*/
|
||||
public static filterAssignments(assignments: any): Assignment[]
|
||||
{
|
||||
let result: Assignment[] = [];
|
||||
|
||||
assignments.assignments.forEach((assignment: any) =>
|
||||
{
|
||||
result.push(
|
||||
{
|
||||
id: assignment.assignment_id,
|
||||
type: assignment.assignment_type,
|
||||
typeId: assignment.assignment_type_id,
|
||||
description: assignment.assignment_description,
|
||||
date: assignment._date,
|
||||
complete: assignment.completion_status,
|
||||
include: assignment.include_in_calculated_grade == 1,
|
||||
display: assignment.display_grade == 1,
|
||||
|
||||
unread: assignment.is_unread == 1,
|
||||
|
||||
scoreMax: assignment.maximum_score,
|
||||
score: +assignment.raw_score
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"defaultSeverity": "warning",
|
||||
"linterOptions": {
|
||||
"exclude": [
|
||||
"node_modules/**"
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"indent": [true, "spaces", 4],
|
||||
"curly": false,
|
||||
"interface-name": false,
|
||||
"no-consecutive-blank-lines": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"ordered-imports": false,
|
||||
"quotemark": [true, "single"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports =
|
||||
{
|
||||
devServer:
|
||||
{
|
||||
disableHostCheck: true,
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user