Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Commits on Source (2)
Showing
with 756 additions and 121 deletions
# Score API
## Update Score
- Endpoint: `/score/?studentId=<studentId>&courseClassId=<courseClassId>`
- HTTP Method: `PUT`
- Request Body:
```json
{
"score": "A"
}
```
- Response Body (Success):
```json
{
"id": "e2eeb6fe-7a73-47e0-b35d-27b53107cd92",
"attendancePercentage": 0,
"score": "A",
"status": "UNAPPROVED",
"createdAt": "2021-04-07T17:11:44.916Z",
"updatedAt": "2021-04-07T17:15:57.297Z",
"courseClassId": "863cd226-36c7-41c1-a83e-f540c752284a",
"studyPlanId": "3b2b43e9-f7c4-4e5e-8ddd-3cacb581dee5",
"studyPlan": {
"id": "3b2b43e9-f7c4-4e5e-8ddd-3cacb581dee5",
"startYear": 2021,
"creditsTotal": 11,
"semester": "1",
"status": "FINAL",
"notes": "A",
"createdAt": "2021-03-24T19:54:29.523Z",
"updatedAt": "2021-03-30T14:14:06.036Z",
"studentId": "aab0fa6a-b521-4a19-9106-d50cc01e4bf3"
}
}
```
......@@ -15,6 +15,7 @@
"chai": "^4.3.0",
"chai-as-promised": "^7.1.1",
"chai-http": "^4.3.0",
"chart.js": "^3.0.2",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
......
......@@ -2,6 +2,7 @@
const {
Student,
} = require('../models/index');
const { updateStudyPlanCourse } = require('../util/db/study-plan');
const {
NotExistError
......@@ -83,3 +84,17 @@ exports.updateStudentData = async(req, res) => {
});
}
};
exports.updateStudentScore = async (req, res) => {
try {
const { studyPlanCourse, changeset } = req.body;
const updatedStudyPlanCourse = await updateStudyPlanCourse(
studyPlanCourse,
changeset
);
res.json(updatedStudyPlanCourse);
} catch (error) {
handleRequestWithInternalServerError(res, error);
}
};
const EndpointEnum = Object.freeze({
name: 'EndpointEnum',
enums: {
AUTH: '/auth',
LECTURER: '/lecturer',
STUDENT: '/student',
FACULTY: '/faculty',
MAJOR: '/major',
SKILL_GROUP: '/skillgroup',
COURSE_CLASS: '/course-class',
COURSE: '/course',
STUDY_PLAN: '/study-plan',
COURSE_CLASS_MEETING: '/course-class-meeting',
}
})
name: "EndpointEnum",
enums: {
AUTH: "/auth",
LECTURER: "/lecturer",
STUDENT: "/student",
FACULTY: "/faculty",
MAJOR: "/major",
SKILL_GROUP: "/skillgroup",
COURSE_CLASS: "/course-class",
COURSE: "/course",
STUDY_PLAN: "/study-plan",
COURSE_CLASS_MEETING: "/course-class-meeting",
SCORE: "/score",
},
});
module.exports = EndpointEnum;
......@@ -17,6 +17,7 @@ const courseClassRouter = require('./routes/courseclass');
const courseRouter = require('./routes/course');
const studyPlanRouter = require('./routes/study-plan');
const courseClassMeetingRouter = require('./routes/course-class-meeting');
const scoreRouter = require('./routes/score');
require('./hooks/study-plan');
require('./hooks/course-class');
......@@ -34,6 +35,7 @@ app.use(EndpointEnum.COURSE_CLASS, courseClassRouter);
app.use(EndpointEnum.COURSE, courseRouter);
app.use(EndpointEnum.STUDY_PLAN, studyPlanRouter);
app.use(EndpointEnum.COURSE_CLASS_MEETING, courseClassMeetingRouter);
app.use(EndpointEnum.SCORE, scoreRouter);
let appServer = app.listen(port, host, () => console.log(`SIX backend service listening at http://${host}:${port}`));
......
"use strict";
const {
handleRequestWithInvalidRequestBody,
checkRequiredParameter,
handleRequestWithInternalServerError,
handleRequestWithResourceItemNotFound,
} = require("../util/common");
const { getStudyPlanCourse } = require("../util/db/study-plan");
const {
RequiredParameterUndefinedError,
NotExistError,
} = require("../util/error");
const { StudyPlan } = require("../models/index");
const updateScoreMiddleware = async (req, res, next) => {
try {
const { studentId, courseClassId } = req.query;
const { score } = req.body;
checkRequiredParameter({
studentId,
courseClassId,
score,
});
const studyPlanCourse = await getStudyPlanCourse({
where: {
courseClassId,
},
include: {
model: StudyPlan,
where: {
studentId,
},
},
});
const changeset = {
score,
};
req.body = {
...req.body,
studyPlanCourse,
changeset,
};
next();
} catch (error) {
if (error instanceof NotExistError) {
handleRequestWithResourceItemNotFound(res, error);
} else if (error instanceof RequiredParameterUndefinedError) {
handleRequestWithInvalidRequestBody(res, error);
} else {
handleRequestWithInternalServerError(res, error);
}
}
};
module.exports = {
updateScoreMiddleware,
};
const express = require("express");
const router = express.Router();
const { updateScoreMiddleware } = require("../middleware/score");
const studentController = require("../controllers/student");
router.put("/", updateScoreMiddleware);
router.put("/", studentController.updateStudentScore);
module.exports = router;
......@@ -3,16 +3,26 @@ const User = require('../models/index')['User'];
const Student = require('../models/index')['Student'];
const Lecturer = require('../models/index')['Lecturer'];
const Major = require('../models/index')['Major'];
const chai = require('chai');
const chaiHttp = require('chai-http');
const server = require('../index');
const should = chai.should();
const {
CourseClass,
Course,
StudyPlan,
StudyPlanCourse,
} = require('../models/index');
const {
EndpointEnum
EndpointEnum,
CourseClassStatusEnum,
SemesterEnum,
ScoreEnum,
} = require('../enums/index');
const { RequiredParameterUndefinedError } = require('../util/error');
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../index');
chai.use(chaiHttp);
const EMAIL = 'ihsan@gmail.com';
......@@ -35,32 +45,82 @@ const SERVER_ERROR_CODE = 500;
const NOT_EXIST_ERROR_CODE = 404;
const ERROR = "Error";
const START_YEAR = 2021;
const SEMESTER = SemesterEnum.ODD;
const COURSE_CODE = "IF2036";
const COURSE_NAME = "Rekayasa Perangkat Lunak";
const COURSE_CREDIT = 3;
const NOTES = 'LGTM';
const SUCCESS_CODE = 200;
const REQUEST_ERROR = 400;
describe('Student Test', () => {
let user, availableCourseClass, unavailableCourseClass, student, studyPlan, studyPlanCourse;
beforeEach(async() => {
await User.destroy({where: {}});
await Student.destroy({where: {}});
});
afterEach(() => {
server.close();
});
await CourseClass.destroy({where: {}});
await Course.destroy({where: {}});
await Lecturer.destroy({where: {}});
await Student.destroy({where: {}});
await StudyPlan.destroy({where: {}});
describe('/GET/:id student', () => {
let user;
const course = await Course.create({
code: COURSE_CODE,
name: COURSE_NAME,
credits: COURSE_CREDIT
});
beforeEach(async() => {
user = await User.create({
user = await User.create({
email: EMAIL,
hashed_password: PASSWORD,
salt: SALT,
});
});
await Student.create({
student = await Student.create({
userId: user.id,
});
const lecturer = await Lecturer.create({
userId: user.id,
});
});
availableCourseClass = await CourseClass.create({
startYear: START_YEAR,
status: CourseClassStatusEnum.OPEN,
semester: SEMESTER,
courseId: course.id,
lecturerId: lecturer.id
});
unavailableCourseClass = await CourseClass.create({
startYear: START_YEAR,
status: CourseClassStatusEnum.CLOSED,
semester: SEMESTER,
courseId: course.id,
lecturerId: lecturer.id
});
studyPlan = await StudyPlan.create({
studentId: student.id,
startYear: START_YEAR,
semester: SemesterEnum.EVEN,
notes: NOTES
});
studyPlanCourse = await StudyPlanCourse.create({
studyPlanId: studyPlan.id,
courseClassId: availableCourseClass.id,
});
});
afterEach(() => {
server.close();
});
describe('/GET/:id student', () => {
it('student get by user id success', (done) => {
chai.request(server)
.get(`${EndpointEnum.STUDENT}/${user.id}`)
......@@ -256,4 +316,78 @@ describe('Student Test', () => {
});
});
});
describe("Update student score", () => {
let error, request;
beforeEach(() => {
error = new RequiredParameterUndefinedError();
request = {
studentId: student.id,
courseClassId: availableCourseClass.id,
score: ScoreEnum.A.name,
};
});
describe("Correct path", () => {
it("should return correct student data when studentId, courseClassId, and score valid", (done) => {
chai
.request(server)
.put(`${EndpointEnum.SCORE}/?studentId=${request.studentId}&courseClassId=${request.courseClassId}`)
.send(request)
.end((_, res) => {
res.should.have.status(SUCCESS_CODE);
res.body.should.have.property("id").to.not.equal(null);
res.body.should.have.property("attendancePercentage").to.not.equal(null);
res.body.should.have.property("score").to.not.equal(null);
res.body.should.have.property("score").to.equal(request.score);
res.body.should.have.property("status").to.not.equal(null);
res.body.should.have.property("courseClassId").to.not.equal(null);
res.body.should.have.property("studyPlanId").to.not.equal(null);
res.body.should.have.property("StudyPlan").to.not.equal(null);
done();
});
});
});
describe("Error path", () => {
it('should throw error when no studentId passed', (done) => {
chai.request(server)
.put(`${EndpointEnum.SCORE}/?courseClassId=${request.courseClassId}`)
.send(request)
.end((_, res) => {
res.should.have.status(REQUEST_ERROR);
res.body.should.have.property("name").to.equal(error.name);
res.body.should.have.property('message');
done();
});
});
it('should throw error when no courseClassId passed', (done) => {
chai.request(server)
.put(`${EndpointEnum.SCORE}/?studentId=${request.studentId}`)
.send(request)
.end((_, res) => {
res.should.have.status(REQUEST_ERROR);
res.body.should.have.property("name").to.equal(error.name);
res.body.should.have.property('message');
done();
});
});
it('should throw error when no score passed', (done) => {
const { score, ...clonedRequest } = request;
chai.request(server)
.put(`${EndpointEnum.SCORE}/?studentId=${request.studentId}&courseClassId=${request.courseClassId}`)
.send(clonedRequest)
.end((_, res) => {
res.should.have.status(REQUEST_ERROR);
res.body.should.have.property("name").to.equal(error.name);
res.body.should.have.property('message');
done();
});
});
});
});
});
......@@ -14,7 +14,7 @@ const {
CourseClassStatusEnum,
SemesterEnum,
StudyPlanStatusEnum,
StudyPlanCourseStatusEnum
StudyPlanCourseStatusEnum,
} = require('../enums/index');
let chai = require('chai');
......
......@@ -105,10 +105,28 @@ const recreateAllStudyPlanCourses = async (studyPlanId, studyPlanCourses) => {
}
};
const getStudyPlanCourse = async (opts = {}) => {
const studyPlanCourse = await StudyPlanCourse.findOne(opts);
if (!studyPlanCourse) throw new NotExistError();
return studyPlanCourse;
};
const updateStudyPlanCourse = async (studyPlanCourse, changeset) => {
const result = await studyPlanCourse.update(changeset);
await result.reload();
return result;
};
module.exports = {
getStudyPlan,
createStudyPlan,
countTotalCredits,
checkClassAvailability,
updateStudyPlan,
getStudyPlanCourse,
updateStudyPlanCourse,
};
......@@ -1088,6 +1088,11 @@
"postcss": "^7.0.0"
}
},
"@j-t-mcc/vue3-chartjs": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@j-t-mcc/vue3-chartjs/-/vue3-chartjs-0.3.0.tgz",
"integrity": "sha512-3/JhMRpG8CNmXHaG3JXY3978KG3o8YWrAYhLBu8PnjfKYB0hlVVb5OUtA2eKLqvEG8AVE6HpC8EKqB/XsK8MLQ=="
},
"@mrmlnc/readdir-enhanced": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
......@@ -1748,16 +1753,6 @@
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"cacache": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
......@@ -1784,34 +1779,6 @@
"unique-filename": "^1.1.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"fs-extra": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
......@@ -1823,25 +1790,6 @@
"universalify": "^0.1.0"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"ssri": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz",
......@@ -1852,16 +1800,6 @@
"minipass": "^3.1.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"terser-webpack-plugin": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
......@@ -1878,18 +1816,6 @@
"terser": "^4.6.12",
"webpack-sources": "^1.4.3"
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.2.0",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.2.0.tgz",
"integrity": "sha512-TitGhqSQ61RJljMmhIGvfWzJ2zk9m1Qug049Ugml6QP3t0e95o0XJjk29roNEiPKJQBEi8Ord5hFuSuELzSp8Q==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
}
}
},
......@@ -3325,6 +3251,32 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"chart.js": {
"version": "2.9.4",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz",
"integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==",
"requires": {
"chartjs-color": "^2.1.0",
"moment": "^2.10.2"
}
},
"chartjs-color": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz",
"integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==",
"requires": {
"chartjs-color-string": "^0.6.0",
"color-convert": "^1.9.3"
}
},
"chartjs-color-string": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
"integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
"requires": {
"color-name": "^1.0.0"
}
},
"check-types": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz",
......@@ -3728,7 +3680,6 @@
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
......@@ -3736,8 +3687,7 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"color-string": {
"version": "1.5.4",
......@@ -8649,6 +8599,11 @@
"minimist": "^1.2.5"
}
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
......@@ -12836,6 +12791,87 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.2.0",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.2.0.tgz",
"integrity": "sha512-TitGhqSQ61RJljMmhIGvfWzJ2zk9m1Qug049Ugml6QP3t0e95o0XJjk29roNEiPKJQBEi8Ord5hFuSuELzSp8Q==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-router": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.3.tgz",
......
......@@ -8,10 +8,12 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@j-t-mcc/vue3-chartjs": "^0.3.0",
"@popperjs/core": "^2.9.1",
"aos": "^2.3.4",
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"chart.js": "^2.9.0",
"core-js": "^3.6.5",
"jquery": "^3.5.1",
"popper.js": "^1.16.1",
......
<script>
import { Line } from "vue-chartjs";
Line.defaults.global.legend.display = false;
export default {
extends: Line,
props: ["data", "options"],
mounted() {
this.renderChart(this.data, this.options);
}
};
</script>
.container-study-progression {
.no-border {
border-top: 0cm;
margin-bottom: 20px;
padding: 0px;
}
.title-no-border {
color: #8bbfe2;
padding-bottom: 0rem;
}
.card-body-no-border {
padding: 0rem;
}
.body-no-border {
display: inline;
}
.table-transcript {
margin: 0cm;
}
.block_container {
display: inline-block;
justify-content: center;
}
.card-graph {
margin-bottom: 20px;
}
}
canvas {
margin: 18px 0px;
}
......@@ -3,19 +3,291 @@
<div class="col-auto p-0">
<sidebar />
</div>
<div class="col-auto p-4 container-study-progression">
<h1>Perkembangan Studi (Coming Soon)</h1>
<!-- @TODO: code here -->
<div class="col p-4 container-study-progression">
<h1 data-aos="fade-down" data-aos-duration="1500" class="mb-4">
Perkembangan Studi
</h1>
<!-- container study progression -->
<div class="row">
<div class="col-md-4">
<!-- card IPK -->
<div
data-aos="fade-up"
data-aos-duration="500"
class="card p-3 no-border"
>
<div class="card-body-no-border">
<div class="row">
<div class="col">
<h3
data-aos="fade-in"
data-aos-offset="300"
data-aos-easing="ease-in-sine"
class="title-no-border"
>
IP & IPK
</h3>
<h1 class="text-blue-six" style="display:inline;">
3.99 /
</h1>
<h3 class="text-blue-six" style="display:inline;">
3.99
</h3>
</div>
</div>
</div>
</div>
<!-- card NR -->
<div
data-aos="fade-up"
data-aos-duration="500"
class="card p-3 no-border"
>
<div class="card-body-no-border">
<div class="row">
<div class="col">
<h3
data-aos="fade-in"
data-aos-offset="300"
data-aos-easing="ease-in-sine"
class="title-no-border"
>
NR (1-2020/2021)
</h3>
<h1 class="text-blue-six" style="display:inline;">
4.00 /
</h1>
<h3 class="text-blue-six" style="display:inline;">
12 SKS
</h3>
</div>
</div>
</div>
</div>
<!-- card credits pass -->
<div
data-aos="fade-up"
data-aos-duration="500"
class="card p-3 no-border"
>
<div class="card-body-no-border">
<div class="row">
<div class="col">
<h3
data-aos="fade-in"
data-aos-offset="300"
data-aos-easing="ease-in-sine"
class="title-no-border"
>
SKS LULUS
</h3>
<h1 class="text-blue-six" style="display:inline;">
12 SKS /
</h1>
<h3 class="text-blue-six" style="display:inline;">
NILAI T 0 SKS
</h3>
</div>
</div>
</div>
</div>
</div>
<!-- card graph -->
<div class="card-graph col-md-8">
<div data-aos="fade-up" data-aos-duration="500" class="card p-3">
<div class="card-body">
<div class="row">
<div class="col">
<vue3-chart-js
:id="chart.id"
:type="chart.type"
:options="chart.options"
:data="chart.data"
></vue3-chart-js>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Transkrip Historis -->
<div class="col-md-12">
<div class="card p-3">
<div
data-aos="fade-in"
data-aos-offset="300"
data-aos-easing="ease-in-sine"
class="card-body"
>
<div class="row">
<div class="col"></div>
</div>
<div class="row">
<!-- Table 1 -->
<h3
data-aos="fade-in"
data-aos-offset="300"
data-aos-easing="ease-in-sine"
class="text-blue-six mb-4"
>
Transkrip Historis
</h3>
<!-- Table 1-->
<h5 class="title-no-border">
Semester 1 tahun 2019/2020
</h5>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Kode</th>
<th scope="col">Mata Kuliah</th>
<th scope="col" class="text-center">SKS</th>
<th scope="col" class="text-center">Nilai</th>
<th scope="col" class="text-center">Semester</th>
<th scope="col" class="text-center">Masuk Transkrip</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>IF5122</td>
<td>Pembangunan Perangkat Lunak</td>
<td class="text-center">
2
</td>
<td class="text-center">A</td>
<td class="text-center">
1
</td>
<td class="text-center">
Sudah
</td>
</tr>
<tr>
<th scope="row">1</th>
<td>IF5123</td>
<td>Kualitas Perangkat Lunak</td>
<td class="text-center">
2
</td>
<td class="text-center">A</td>
<td class="text-center">
1
</td>
<td class="text-center">
Sudah
</td>
</tr>
</tbody>
</table>
<!-- Table 2-->
<h5 class="title-no-border">
Semester 2 tahun 2019/2020
</h5>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Kode</th>
<th scope="col">Mata Kuliah</th>
<th scope="col" class="text-center">SKS</th>
<th scope="col" class="text-center">Nilai</th>
<th scope="col" class="text-center">Semester</th>
<th scope="col" class="text-center">Masuk Transkrip</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>IF5122</td>
<td>Pembangunan Perangkat Lunak</td>
<td class="text-center">
2
</td>
<td class="text-center">A</td>
<td class="text-center">
1
</td>
<td class="text-center">
Sudah
</td>
</tr>
<tr>
<th scope="row">1</th>
<td>IF5123</td>
<td>Kualitas Perangkat Lunak</td>
<td class="text-center">
2
</td>
<td class="text-center">A</td>
<td class="text-center">
1
</td>
<td class="text-center">
Sudah
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Sidebar from "@/components/Sidebar/Sidebar";
import Vue3ChartJs from "@j-t-mcc/vue3-chartjs";
export default {
components: { Sidebar },
name: "StudyProgression"
components: { Sidebar, Vue3ChartJs },
name: "StudyProgression",
data() {
return {
chart: {
id: "ipkChart",
type: "line",
data: {
labels: ["1/2019", "2/2019", "1/2020", "2/2020"],
datasets: [
{
label: "IP",
data: [3, 3.5, 3, 4],
fill: false,
borderColor: "#1673B1",
tension: 0.1
}
]
},
options: {
legend: {
display: false
},
scales: {
xAxes: [
{
gridLines: {
display: false
}
}
],
yAxes: [
{
gridLines: {
display: true
}
}
]
},
aspectRatio: 3
}
}
};
}
};
</script>
......