diff --git a/package-lock.json b/package-lock.json index 3504d991dc40e1090e7292d06ef8395100b3e868..18d139d2f09d693e999d59df0a449ac11d2f3fa1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "frontend_idatt2105", "version": "0.1.0", "dependencies": { "axios": "^0.26.1", @@ -12,6 +13,7 @@ "qs": "^6.10.3", "register-service-worker": "^1.7.2", "vue": "^3.2.13", + "vue-mq": "^1.0.1", "vue-router": "^4.0.3", "vuex": "^4.0.0" }, @@ -4198,8 +4200,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -4215,9 +4215,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/ajv-keywords": { "version": "3.5.2", @@ -10836,6 +10834,14 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", @@ -16590,6 +16596,11 @@ } ] }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -17594,6 +17605,14 @@ "node": ">=8" } }, + "node_modules/vue-mq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vue-mq/-/vue-mq-1.0.1.tgz", + "integrity": "sha512-FceZ1tFE0MZ8GroRBKPQWBRy4ZEAa7p5R7cGAzJpGuKPU2AI4ClmE+S6O/yV4jO5271o9tgaUFt7fzUAIf9xOQ==", + "dependencies": { + "json2mq": "^0.2.0" + } + }, "node_modules/vue-router": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.14.tgz", @@ -21034,6 +21053,7 @@ "integrity": "sha512-vf4KqrmuOSnoEYGUiHPeMoxhh6wpiucLWXISn7xYFU80pK1lqcuhbl6tpurAanUIyRO/ENDUQBH7RAdbLNq1bA==", "dev": true, "requires": { + "@babel/core": "^7.12.16", "@babel/helper-compilation-targets": "^7.12.16", "@babel/helper-module-imports": "^7.12.13", "@babel/plugin-proposal-class-properties": "^7.12.13", @@ -21046,6 +21066,7 @@ "@vue/babel-plugin-jsx": "^1.0.3", "@vue/babel-preset-jsx": "^1.1.2", "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.8.3", "core-js-compat": "^3.8.3", "semver": "^7.3.4" }, @@ -21213,6 +21234,7 @@ "@vue/cli-shared-utils": "^5.0.4", "babel-jest": "^27.1.0", "deepmerge": "^4.2.2", + "jest": "^27.1.0", "jest-serializer-vue": "^2.0.2", "jest-transform-stub": "^2.0.0", "jest-watch-typeahead": "^1.0.0" @@ -21887,14 +21909,15 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "requires": {}, + "requires": { + "ajv": "^8.0.0" + }, "dependencies": { "ajv": { - "version": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, - "optional": true, - "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -21906,9 +21929,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "optional": true, - "peer": true + "dev": true } } }, @@ -26804,6 +26825,14 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=", + "requires": { + "string-convert": "^0.2.0" + } + }, "json5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", @@ -30926,6 +30955,11 @@ } } }, + "string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=" + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -31669,6 +31703,14 @@ } } }, + "vue-mq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vue-mq/-/vue-mq-1.0.1.tgz", + "integrity": "sha512-FceZ1tFE0MZ8GroRBKPQWBRy4ZEAa7p5R7cGAzJpGuKPU2AI4ClmE+S6O/yV4jO5271o9tgaUFt7fzUAIf9xOQ==", + "requires": { + "json2mq": "^0.2.0" + } + }, "vue-router": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.14.tgz", diff --git a/package.json b/package.json index f08d687bde433444d1fb66c626c56b3717210fab..951f15e6df0cc3fe7104ce4fa77af620ca1777f6 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "qs": "^6.10.3", "register-service-worker": "^1.7.2", "vue": "^3.2.13", + "vue-mq": "^1.0.1", "vue-router": "^4.0.3", "vuex": "^4.0.0" }, diff --git a/src/App.vue b/src/App.vue index d4e793da42545591c4237eba9252f898346050cd..9af52f95bc205802d500031aaeedd886e68b8484 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,16 @@ <template> + <meta name="viewport" content="user-scalable=no, width=device-width, height=device-height, minimum-scale=1.0, maximum-scale=1.0" /> - <div v-if="$store.state.loggedIn" id="nav"> + + + <div v-if="isLoggedIn" id="nav"> <NavBar /> </div> + <img src="./assets/qsLogo.png" alt=""> + <router-view/> - + </template> @@ -13,63 +18,60 @@ <script> import NavBar from './components/NavBar.vue' +import store from "@/store"; export default { - components: {NavBar}, - - + computed: { + isLoggedIn() { + return this.$store.state.loggedIn + } + } } </script> <style> + #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; -} + margin-left: 10px; + margin-right: 10px; +} -form { - max-width: 25%; /* was 420px */ - margin: 30px auto; - background: white; - text-align: left; - padding: 0px; - border-radius: 10px; + button { + text-decoration: none; + background: #00ce89; + border: 0; + padding: 10px 20px; + color: #26323d; + border-radius: 20px; + font-size: 18px; + height: 40px; } - - form input { - font-size: 20px; - } - - label { - color: #aaa; - display: inline-block; - margin: 25px 0 5px; - font-size: 0.8em; - text-transform: uppercase; - letter-spacing: 1px; - font-weight: bold; - + button:hover { + background: #2c3e50; + cursor: pointer; + color: white; } - input { - display: block; - padding: 10px 6px; - width: 100%; - box-sizing: border-box; - height: 50px; /* edited */ - border: none; - border-bottom: 1px solid #ddd; - color: #555; + button:active { + background: #14406c; + cursor: pointer; + color: white; } - + button:disabled { + background: #34404d; + cursor: default; + color: #a4a4a4; + } </style> diff --git a/src/assets/qsLogo.png b/src/assets/qsLogo.png index 0b7bcc754ce64dcd9a3b1f5a83147a6502380635..aa16d5985b7753bf45337e2b29b91a40ca7090da 100644 Binary files a/src/assets/qsLogo.png and b/src/assets/qsLogo.png differ diff --git a/src/components/ErrorBox.vue b/src/components/ErrorBox.vue index 6780449102237244a0c61e4b98da9b4c200e0985..592ebc3819be3c0de48bd8d5d836bd678b14a55b 100644 --- a/src/components/ErrorBox.vue +++ b/src/components/ErrorBox.vue @@ -1,22 +1,16 @@ <template> <div class="error"> - <h3>Something went wrong</h3> - <p>{{ errorMessage }}</p> + <h3>Error</h3> + <p>{{ message }}</p> </div> </template> <script> export default { - - computed: { - errorMessage: { - get() { - return this.$store.state.message - }, - - } - } + props: { + message: String + }, } </script> @@ -24,19 +18,14 @@ export default { <style scoped> .error{ - - margin: 10, 10; - max-width: 50%; - margin: 30px auto; - margin-top: 0px; background: white; text-align: center; border-radius: 10px; - border: 2px solid #ff0000; + border: 2px solid #de644f; } h3 { - color: #4038D1; + color: #2f2f2f; text-decoration: underline; } @@ -44,4 +33,4 @@ p { color: rgb(0, 0, 0); } -</style> \ No newline at end of file +</style> diff --git a/src/components/Exercise.vue b/src/components/Exercise.vue index 2eba9cb9b42f5cf042c38c65ca71130525f752a6..f0cbcd275ff9c85d91cfc1e8cd6b194614e0427a 100644 --- a/src/components/Exercise.vue +++ b/src/components/Exercise.vue @@ -1,20 +1,20 @@ <template> <div class="exercise" :class="{exerciseComplete: exercise.approved}"> <h3>assignment number: {{ exercise.assignment_number }}</h3> - + <div class="icons"> <span v-if="!exercise.approved" class="material-icons" @click="openQueueForm">person_add</span> <span v-if="exercise.is_complete" class="material-icons">comment</span> </div> - - + + </div> - + <!-- queueForm her --> <QueueForm @queueSubmit="openQueueForm" :exercise="exercise" :subject="subject" v-if="this.queueForm"/> - - + + </template> <script> @@ -35,7 +35,7 @@ export default { openQueueForm() { this.queueForm = !this.queueForm } - + } } @@ -50,10 +50,7 @@ export default { border-radius: 4px; box-shadow: 1px 2px 3px rgba(0,0,0,0.05); border-left: 4px solid #e90074; - width: 100%; background-color: rgb(253, 253, 253); - - width: 100%; display: flex; flex-direction: row; justify-content: space-between; @@ -79,7 +76,7 @@ export default { color: #777; } - - -</style> \ No newline at end of file + + +</style> diff --git a/src/components/Exercises.vue b/src/components/Exercises.vue index 65391b27616bc47088868fb8f4d1a76e1c6c9f26..80b80f7afc282e998a9fac8f8ebc6f88fe54e8e4 100644 --- a/src/components/Exercises.vue +++ b/src/components/Exercises.vue @@ -3,7 +3,7 @@ <div v-for="exercise in exercises" :key="exercise.assignment_number"> <Exercise :exercise="exercise" :subject="subject"/> </div> - + </div> </template> @@ -16,15 +16,11 @@ export default { components: {Exercise}, props: ['exercises', 'subject'] - + } </script> <style> -.exercises { - padding: 10px; -} - -</style> \ No newline at end of file +</style> diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue index 523dd3f25f73b040346516dec20c7d1a4e9b5342..efa7f061145863467ee4a236f3235a37471927ba 100644 --- a/src/components/LoginForm.vue +++ b/src/components/LoginForm.vue @@ -1,23 +1,22 @@ <template> - <div v-if="$store.state.message != ''"> - <ErrorBox /> - </div> - <form @submit.prevent="handleLogin" > - - <label>Email:</label> - <input type="email" required v-model="email"> - - <label>Password:</label> - <input type="password" required v-model="password"> - - <div class="loginDiv"> - <button type="button" @click="handleLogin">Login</button> - <p @click="forgotPassword" class="forgot">Forgot password?</p> + <div class="loginContainer"> + + <div v-if="error"> + <ErrorBox :message="error"/> </div> - </form> + <form @submit.prevent="handleLogin" > + + <label for="username">Email (NTNU-Email):</label> + <input id="username" type="email" required v-model="email"> + + <label for="password">Password:</label> + <input type="password" id="password" required v-model="password"> + + <button type="submit" @click="handleLogin">Logg inn</button> + </form> + </div> - </template> <script> @@ -30,9 +29,10 @@ export default { data() { return { - email: '', - password: '', - token: '' + error: null, + email: '', + password: '', + token: '' } }, @@ -44,27 +44,73 @@ export default { this.$store.commit('setToken', ans.data) this.$store.commit('setLogin', this.email, this.password) this.$store.commit('setMessage', '') - this.$router.push('/Subjects') + this.$router.push('/Subjects/student') }) .catch(err => { - console.log(err) - this.$store.commit('setLogin', '', '') - this.$store.commit('setMessage', 'Invalid email or password') - }) + if(err.response) { + this.error = err.response.data; + } else { + this.error = err + } + + console.log(err.response) + this.$store.commit('setLogin', '', '') + }) + + }, - }, - forgotPassword() { this.$store.commit('forgotPassword') } }, +} +</script> + +<style> + +.loginContainer { + width: 95%; + max-width: 416px; + margin: auto; +} +button { + margin-top: 16px; + width: 100%; +} +form { + margin: 30px auto; + background: white; + text-align: left; + border-radius: 10px; +} +form input { + font-size: 20px; } -</script> -<style> - -</style> \ No newline at end of file +label { + color: #333333; + display: inline-block; + margin: 25px 0 5px; + font-size: 0.8em; + text-transform: uppercase; + letter-spacing: 1px; + font-weight: bold; + +} + +input { + display: block; + padding: 10px 6px; + width: 100%; + box-sizing: border-box; + height: 50px; /* edited */ + border: none; + border-bottom: 1px solid #ddd; + color: #414141; +} + +</style> diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index c5ad77ee5b87ac8b97f4e8700006485eee811ee6..9b1d9f6d2c9079bc6f7f022aae06725d7eee1551 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -1,10 +1,12 @@ <template> - <div id="navBar"> - <router-link :to="{name: 'Subjects'}">Subjects</router-link> + <div> + <div id="navBar"> + <router-link :to="{name: 'Subjects', params: {tab: 'student'}}">Subjects</router-link> <router-link :to="{name: 'Settings'}">Settings</router-link> <router-link @click="logOut" :to="{name: 'Login'}">Log out</router-link> + </div> + <hr> </div> - <hr> </template> <script> @@ -24,7 +26,7 @@ export default { #navBar { padding: 15px; - + } #navBar a { @@ -47,4 +49,4 @@ hr { background-color:#2c3e50; } -</style> \ No newline at end of file +</style> diff --git a/src/components/OptionBar.vue b/src/components/OptionBar.vue index 4ad2590acf09817d02e57d917f5d5fe11c136010..92fbdf167dbf4da7a854f3e7d6cc243ad79af35d 100644 --- a/src/components/OptionBar.vue +++ b/src/components/OptionBar.vue @@ -1,15 +1,26 @@ <!-- The options navbar 'Student, Stud.ass, Arkivert' --> <template> <div class="optionBar"> - <a :class="{active: $store.state.currentOption == 'Student'}" @click="$store.commit('setCurrentOption', 'Student')">Student</a> - <a :class="{active: $store.state.currentOption == 'Stud.ass'}" @click="$store.commit('setCurrentOption', 'Stud.ass')">Stud.ass</a> - <a :class="{active: $store.state.currentOption == 'Arkivert'}" @click="$store.commit('setCurrentOption', 'Arkivert')">Arkivert</a> + <a :class="{active: this.tab === 'student'}" @click="navigateToTab('student')">Student</a> + <a :class="{active: this.tab === 'studentassistant'}" @click="$store.commit('setActiveOption', 'Stud.ass')">Stud.ass</a> + <a :class="{active: this.tab === 'archived'}" @click="$store.commit('setActiveOption', 'Arkivert')">Arkivert</a> </div> </template> <script> export default { - + props: { + tab: { + type: String, + required: true + } + }, + + methods: { + navigateToTab(name) { + this.$router.push({name: 'Subject', params: {tab: name }}) + } + } } </script> @@ -38,4 +49,4 @@ export default { border-bottom: 2px solid #00ce89; } -</style> \ No newline at end of file +</style> diff --git a/src/components/Queue.vue b/src/components/Queue.vue index e9121a7699935a9a062e2733acbe48e1e33c8478..6f15460a3b3297fcf3a081efc9b471494e13db10 100644 --- a/src/components/Queue.vue +++ b/src/components/Queue.vue @@ -1,7 +1,6 @@ <template> - + <div class="queue"> - <h1>{{ this.subject_name }}</h1> <ul> <li v-for="student in this.studentQueue" :key="student.student_user_id"> <Student :student="student" /> @@ -18,11 +17,11 @@ import Student from './Student.vue' export default { components: {Student}, - + props: ['studentQueue'], - - + + } </script> diff --git a/src/components/SingleSubject.vue b/src/components/SingleSubject.vue index 70c9db46806510525a7dceb0ab9e73f2716ad8e6..c8263bbe12006d2f07003018be4f5f8bb5471be1 100644 --- a/src/components/SingleSubject.vue +++ b/src/components/SingleSubject.vue @@ -1,75 +1,93 @@ <template> - <div class="subjectHeader"> - <h1>{{ subject.subject.subjectInfo.name }}</h1> - <h2>{{ subject.subject.subjectInfo.subject_code }}</h2> - </div> - - <div class="subjectOptions"> - - <div v-if="$store.state.currentOption == 'Student'"> - <router-link :to="{ name: 'Info', params: { subject_code: subject.subject_code, semester: subject.semester }}"> <!-- send prop 'subject.exercises' so i can loop it --> - <button @click="$store.commit('setCurrentSubject', subject)">Øvinger</button> - </router-link> - <button>Status</button> - <button @click="chatClick">Chat</button> + <div class="subjectItemContainer" v-bind:class="{open : subject.active, closed : !subject.active, disabled : !subject.active}"> + + <div class="subjectHeader"> + <h2>{{ subject.subject.subjectInfo.name }}</h2> + <h3>{{ subject.subject_code }} - {{ subject.semester }}</h3> + <h4 v-if="subject.active">Queue is open</h4> </div> - <div v-else-if="$store.state.currentOption == 'Stud.ass'"> - <router-link :to="{ name: 'Info', params: { subject_code: subject.subject_code }}"> <!-- send prop 'subject.exercises' so i can loop it --> - <button @click="$store.commit('setCurrentSubject', subject)">Åpne kø</button> - </router-link> + <div class="subjectOptions"> + <button @click="pushToQueueScreen()">View Queue</button> + <button @click="chatClick">Chat</button> + <!--<button @click="">Åpne kø</button>--> </div> - - + </div> </template> <script> +export default { + props: { + subject: { + type: Object, + required: true + } + }, + + methods: { + pushToQueueScreen() { + this.$router.push({name: 'Info', params: {subject_code: "IDATT2105", semester: "V22"}}) + } + } +} +</script> -import SubjectQueueService from '../service/SubjectService.js' +<style> -export default { - props: ['subject'], +.disabled { + opacity: 75%; +} - methods: { - - - - }, +.disabled:hover { + opacity: 100%; +} + +.subjectItemContainer { + background: white; + border-radius: 4px; + box-shadow: 1px 2px 3px rgba(0,0,0,0.05); + border: #e0e0e0 1px solid; + border-left: 10px solid #000000; - + display: grid; + padding: 22px; } -</script> -<style> +.open { + border-left-color: #59d329; +} -.subjectOptions{ - text-align: center; - margin: 40px auto; - margin-bottom: 15px; +.closed { + border-left-color: #e33838; } -.subjectOptions button { - display: inline-block; - text-decoration: none; - margin: 0 15px; - background: #00ce89; - border: 0; - padding: 10px 20px; - margin-top: 0px; - color: #2c3e50; - border-radius: 20px; - font-size: 20px; +.archived { + border-left-color: #414141; } +.subjectOptions { + text-align: center; + margin: 10px 0 0; + display: grid; + grid-auto-flow: column; +} + +.subjectOptions { + display: grid; + grid-row-gap: 10px; + grid-column-gap: 10px; +} + + +@media only screen and (max-width: 768px) { + .subjectOptions { + grid-auto-flow: row; + } -.subjectOptions button:hover { - background: #2c3e50; - cursor: pointer; - color: white; } -</style> \ No newline at end of file +</style> diff --git a/src/components/Student.vue b/src/components/Student.vue index e6ea55aa51a7272fe982a89869ddbb09b9e11827..60bab551f078838ad1e8a8851da3a160eec7445e 100644 --- a/src/components/Student.vue +++ b/src/components/Student.vue @@ -11,7 +11,7 @@ export default { props: ['student'], - + } </script> @@ -24,12 +24,7 @@ export default { border-radius: 4px; box-shadow: 1px 2px 3px rgba(233, 7, 101, 0.05); border-left: 4px solid #e90074; - width: 100%; - background-color: rgb(253, 253, 253); - display: flex; - width: 100%; - display: flex; flex-direction: row; justify-content: space-between; align-items: center; @@ -37,11 +32,11 @@ export default { .studentActive { - + border-left: 4px solid #00e94e; - - - + + + } -</style> \ No newline at end of file +</style> diff --git a/src/components/SubjectList.vue b/src/components/SubjectList.vue new file mode 100644 index 0000000000000000000000000000000000000000..77ac71a322790ac715a5aa1bf750004f2b9b7230 --- /dev/null +++ b/src/components/SubjectList.vue @@ -0,0 +1,100 @@ +<template> + <div class="container"> + + <div class="activeQueuesContainer" v-if="this.getActiveSubjects"> + <h1>Active Queues</h1> + <ul> + <li v-for="subject in this.getActiveSubjects" :key="subject.subject_code"> + <SingleSubject :subject="subject" /> + </li> + </ul> + </div> + + <div class="inactiveQueuesContainer" v-if="this.getInactiveSubjects"> + <h1>Inactive Queues</h1> + <ul> + <li v-for="subject in this.getInactiveSubjects" :key="subject.subject_code"> + <SingleSubject :subject="subject" /> + </li> + </ul> + </div> + </div> +</template> + +<script> + +import SubjectService from "@/service/SubjectService"; +import SingleSubject from "@/components/SingleSubject"; +import store from '@/store/index.js'; + +export default { + components: {SingleSubject}, + + data() { + return { + subjects: null + } + }, + + computed: { + getActiveSubjects() { + if(this.subjects != null) { + return this.subjects.filter(s => { + return s.active && !s.archived; + }) + } else { + return null; + } + }, + getInactiveSubjects() { + if(this.subjects != null) { + return this.subjects.filter(s => { + return !s.active && !s.archived; + }) + } else { + return null; + } + }, + }, + + mounted () { + console.log("Before load") + + return SubjectService.getStudentAssistantSubjectQueues(store.state.token) + .then(res => { + console.log(res.data) + this.subjects = res.data; + console.log(this.subjects) + }) + .catch(() => next({name: "Error"})) + }, + +} + +</script> + +<style> + +ul { + padding: 0; +} + +li { + list-style-type: none; + margin-bottom: 15px; +} + +.container { + max-width: 950px; + margin: 0 auto; +} + +.inactiveQueuesContainer { + margin-top: 84px; +} + +.container h1 { + text-decoration: underline; +} + +</style> diff --git a/src/main.js b/src/main.js index ff1fa85763070c2ca76819d302529bf43f630f9c..041fa11d6fd20e620b5bd0556b39a2f2d41c179a 100644 --- a/src/main.js +++ b/src/main.js @@ -4,6 +4,4 @@ import './registerServiceWorker' import router from './router' import store from './store' - - createApp(App).use(store).use(router).mount('#app') diff --git a/src/router/index.js b/src/router/index.js index ab65ef0d89b7d6a62cba1e876edc2cc4c28f9ae8..6495713ad7448553a3fb3d03ca9ba8104e96bb18 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -4,7 +4,7 @@ import Settings from '../views/Settings.vue' import Subjects from '../views/Subjects.vue' import NotFound from '../views/NotFound.vue' import Info from '../views/Info.vue' - +import store from "@/store"; const routes = [ { @@ -15,19 +15,28 @@ const routes = [ { path: '/Settings', name: 'Settings', - component: Settings + component: Settings, + meta: { + requiresAuth: true + } }, { - path: '/Subjects', + path: '/Subjects/:tab', name: 'Subjects', - component: Subjects + component: Subjects, + meta: { + requiresAuth: true + } }, - + { //path: '/Subjects/Exercises/:subject_id', // dynamic path path: '/Subjects/:subject_code/:semester/Info', name: 'Info', - component: Info + component: Info, + meta: { + requiresAuth: true + } }, // Redirect any none-existing path to 404 page { @@ -42,4 +51,17 @@ const router = createRouter({ routes }) +router.beforeEach((to, from, next) => { + if (to.matched.some(record => record.meta.requiresAuth)) { + + if (store.state.token === '') { + next({ name: 'Login' }) + } else { + next() + } + } else { + next() + } +}) + export default router diff --git a/src/service/ApiService.js b/src/service/ApiService.js index 9d51914355d3f575f89d3aac850e5e7176184dcb..5c99c3b0c864c5f29e028f2acb9a0bd945272671 100644 --- a/src/service/ApiService.js +++ b/src/service/ApiService.js @@ -11,4 +11,4 @@ export default { } }) } -} \ No newline at end of file +} diff --git a/src/store/index.js b/src/store/index.js index aa72faa8b36623f4727e865b8dc9941ee90de39a..968a515b6645f83d1a40505a2f876b91f4e93a12 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -3,10 +3,8 @@ import { createStore } from 'vuex' export default createStore({ state: { // variables accessable from anywhere within the app - currentView:'', // den her Email: '', // den her - password: '', // den her token: '', loggedIn: false, // den her forgotPassword: false, @@ -14,34 +12,19 @@ export default createStore({ currentOption: 'Student', // den her currentSubject: null, subjects: [], - - - }, - getters: { // data from state can be accessed directly. But with getters we can do some filtering before getting the returned value(s). - getSubject(code, semester) { - for(subject in subjects) { - if(subject.semester == semester && subject.subjectInfo.subject_code == code) { - return subject - } - } - return null - } + + }, + mutations: { // methods to change variables within the state setEmail(state, newEmail) { state.currentEmail = newEmail }, - setPassword(state, newPassword) { - state.currentPassword = newPassword - }, - setPassword(state) { - state.forgotPassword = !state.forgotPassword - state.message = '' - }, + setCurrentSubject(state, subject) { state.currentSubject = subject }, - + setToken(state, newToken) { state.token = newToken }, @@ -72,10 +55,10 @@ export default createStore({ setCurrentOption(state, option) { state.currentOption = option } - + }, - + modules: { // to split the store into modules } diff --git a/src/views/Info.vue b/src/views/Info.vue index 7f42e96cbd0b97bc30e76994745d69dba7aa9647..455541f1731587ec2c7912bd74910c8e09ff0e84 100644 --- a/src/views/Info.vue +++ b/src/views/Info.vue @@ -1,22 +1,21 @@ <template> - - + <div class="info"> <div class="queue"> - + <h2>{{ this.subject_name }}</h2> <Queue :studentQueue="studentQueue" /> </div> - + <div class="exercises"> - + <h2>{{ this.subject_name }}</h2> <Exercises :exercises="exercises" :subject="subject"/> </div> </div> - - - + + + </template> <script> @@ -36,7 +35,7 @@ export default { return { semester: '', subject_code: '', - subject_name: '', + subject_name: '', studentQueue: null, subject: null, exercises: null @@ -44,44 +43,44 @@ export default { }, mounted() { - + SubjectQueueService.getSubject(this.getSubjectCode, this.getSemester, this.$store.state.token). then(ans => { - + this.semester = ans.data.semester this.subject_code = ans.data.subjectInfo.subject_code - this.subject_name = ans.data.subjectInfo.name - + this.subject_name = ans.data.subjectInfo.name + this.subject = ans.data console.log('this subject = ', this.subject) AssingmentService.getAssignmentsForStudent(ans.data, this.$store.state.token). then(ans => { - + this.exercises = ans.data console.log('exercises: ', this.exercises) }).catch(err => console.log(err)) - - + + SubjectQueueService.getSubjectQueuePositions(ans.data, this.$store.state.token) - .then(ans => { - this.studentQueue = ans.data + .then(ans => { + this.studentQueue = ans.data }).catch(err => console.log(err)) }).catch(err => console.log(err)) - - + + }, computed: { - + getSubjectCode() { - + return this.$route.params.subject_code }, @@ -95,28 +94,20 @@ export default { <style> .info { - width: 100%; - display: inline-block; - width: 100%; - display: flex; - flex-direction: row; - justify-content: center; - + display: grid; + grid-auto-flow: column; + grid-column-gap: 32px; } +@media only screen and (max-width: 768px) { -.queue { - margin: 30px; - padding: 10px; - -} + .info { + grid-auto-flow: row; + grid-column-gap: 10px; + + } -.exercises { - margin: 30px; - - margin-left: 100px; - } -</style> \ No newline at end of file +</style> diff --git a/src/views/Login.vue b/src/views/Login.vue index 9c82cf4a66910deec9d22b9927aaa91b9edba607..bb3d4026071be9ed95eb2ca54f76806559423323 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -1,14 +1,14 @@ <template> - <img src="../assets/qsLogo.png" alt=""> <div v-if="$store.state.forgotPassword"> <ForgotPassword /> </div> + <div v-else> <LoginForm /> </div> - - + + </template> <script> @@ -22,7 +22,7 @@ export default { components: {LoginForm, ForgotPassword}, methods: { - + } } @@ -30,41 +30,5 @@ export default { <style> -.loginDiv button{ - background: #4038D1; - border: 0; - padding: 10px 20px; - margin-top: 20px; - color: white; - border-radius: 20px; - font-size: 20px; -} - -.loginDiv button:hover { - cursor: pointer; - background: #1e59b1; -} - -.loginDiv { - width: 100%; - display: flex; - flex-direction: row; - position: relative; -} - -.forgot { - position: absolute; - right: 0; - top: 12px; - color: #4038D1; - text-decoration: underline; - font-size: 20px; -} - -.forgot:hover { - cursor: pointer; - color: #1e59b1; -} - -</style> \ No newline at end of file +</style> diff --git a/src/views/Subjects.vue b/src/views/Subjects.vue index 83a103b6edcd4ffd49c9379cce5679316b68b554..0e9cd048057382e02cb99ea47cee5a556b8d6651 100644 --- a/src/views/Subjects.vue +++ b/src/views/Subjects.vue @@ -1,16 +1,7 @@ <template> - <OptionBar /> - <!-- v-for emner --> - <ul> - <li v-for="subject in this.subjects" :key="subject.subject_code"> - <div class="subjects" v-if="subject.active"> - - <SingleSubject :subject="subject"/> <!-- Every subject has a info-page, which contains queue and exercises--> - - </div> - </li> - </ul> + <OptionBar :tab="getActiveTab" /> + <SubjectList /> </template> @@ -19,31 +10,27 @@ <script> import OptionBar from '../components/OptionBar.vue' -import SingleSubject from '../components/SingleSubject.vue' import SubjectQueueService from '../service/SubjectService.js' +import SubjectList from '../components/SubjectList.vue' export default { name: 'Subjects', - components: {OptionBar, SingleSubject}, + components: {OptionBar, SubjectList}, data() { return { - subjects: [] + activeTab: String } }, mounted() { - SubjectQueueService.getStudentSubjectQueues(this.$store.state.token). - then(ans => { - this.subjects = ans.data - console.log('subjects = ', this.subjects) - }).catch(err => console.log(err)) - }, computed: { - + getActiveTab() { + return this.$route.params.tab + } } }