diff --git a/src/App.vue b/src/App.vue index 9af52f95bc205802d500031aaeedd886e68b8484..48498bf6ce4780fb9f16260fa4c3b39f73f6870c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -45,6 +45,14 @@ export default { } + .box { + 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; + } + button { text-decoration: none; background: #00ce89; @@ -56,6 +64,16 @@ export default { height: 40px; } + .button-yellow { + background: #e5e02d; + color: #2c2c2c; + } + + .button-red { + background: #e35c5c; + color: #ffffff; + } + button:hover { background: #2c3e50; cursor: pointer; @@ -74,4 +92,7 @@ export default { color: #a4a4a4; } + .underline { + text-decoration: underline; + } </style> diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue index efa7f061145863467ee4a236f3235a37471927ba..841258f1c0a6051262b4caa023f5ad7f8b0cf3dd 100644 --- a/src/components/LoginForm.vue +++ b/src/components/LoginForm.vue @@ -13,7 +13,7 @@ <label for="password">Password:</label> <input type="password" id="password" required v-model="password"> - <button type="submit" @click="handleLogin">Logg inn</button> + <button type="submit">Logg inn</button> </form> </div> @@ -23,6 +23,7 @@ import ErrorBox from './ErrorBox.vue' import LoginService from '../service/LoginService.js' +import UserAccountService from "@/service/UserAccountService"; export default { components: {ErrorBox}, @@ -41,10 +42,33 @@ export default { handleLogin() { LoginService.getToken(this.email, this.password). then(ans => { - this.$store.commit('setToken', ans.data) - this.$store.commit('setLogin', this.email, this.password) - this.$store.commit('setMessage', '') + this.$store.commit('setToken', ans.data) + this.$store.commit('setLogin', this.email) + + + UserAccountService.getRolesForLoggedInUser(ans.data).then(roles => { + this.$store.commit('setActiveRoles', roles.data) + + UserAccountService.getUserAccountDetails(this.email, ans.data).then(usr => { + this.$store.commit('setUserAccountDetails', usr.data) + + }).catch(err =>{ + console.log(err.response) + this.$store.commit('setLogin', '', '') + }) + this.$router.push('/Subjects/student') + }).catch(err => { + if(err.response) { + this.error = err.response.data; + } else { + this.error = err + } + + console.log(err.response) + this.$store.commit('setLogin', '', '') + }) + }) .catch(err => { if(err.response) { diff --git a/src/components/OptionBar.vue b/src/components/OptionBar.vue index 92fbdf167dbf4da7a854f3e7d6cc243ad79af35d..4ad215347638d228523e0f01b2e1ceeaf5772a4d 100644 --- a/src/components/OptionBar.vue +++ b/src/components/OptionBar.vue @@ -1,9 +1,10 @@ <!-- The options navbar 'Student, Stud.ass, Arkivert' --> <template> <div class="optionBar"> - <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> + <a :class="{active: this.tab === 'student'}" @click="navigateToTab('student')" v-if='showStudentTab'>Queues</a> + <a :class="{active: this.tab === 'studentassistant'}" @click="navigateToTab('studentassistant')" v-if="showStudentAssistantTab">Stud.ass Panel</a> + <a :class="{active: this.tab === 'archived'}" @click="$store.commit('setActiveOption', 'Arkivert')">Archived</a> + <a :class="{active: this.tab === 'archived'}" @click="$store.commit('setActiveOption', 'Arkivert')" v-if="showAdminTab">Admin Panel</a> </div> </template> @@ -16,9 +17,21 @@ export default { } }, + computed: { + showStudentTab() { + return this.$store.state.userRoles.includes("ROLE_STUDENT") + }, + showStudentAssistantTab() { + return this.$store.state.userRoles.includes("ROLE_STUDENT_ASSISTANT") + }, + showAdminTab() { + return this.$store.state.userRoles.includes("ROLE_TEACHER") + }, + }, + methods: { navigateToTab(name) { - this.$router.push({name: 'Subject', params: {tab: name }}) + this.$router.push({name: 'Subjects', params: {tab: name }}) } } } diff --git a/src/components/Queue.vue b/src/components/Queue.vue index 6f15460a3b3297fcf3a081efc9b471494e13db10..36fef668d5b403c6fc08d0e59bec240846236a36 100644 --- a/src/components/Queue.vue +++ b/src/components/Queue.vue @@ -1,12 +1,15 @@ <template> - <div class="queue"> + <div class="queue" v-if="studentQueue==null || studentQueue.length>0" > <ul> <li v-for="student in this.studentQueue" :key="student.student_user_id"> - <Student :student="student" /> + <Student :student="student" :show-remove-button="showToolButtons" :show-pick-button="showToolButtons" :on-pick-button-pressed="onPickButtonPressed" :on-remove-button-pressed="onRemoveButtonPressed"/> </li> </ul> </div> + <div v-else> + <h4>This queue is empty...</h4> + </div> </template> @@ -14,14 +17,26 @@ import Student from './Student.vue' - export default { components: {Student}, - - props: ['studentQueue'], - - - + props: { + studentQueue: { + required: true, + type: Object + }, + showToolButtons: { + default: false, + type: Boolean + }, + onPickButtonPressed: { + type: Function, + default: ()=>{} + }, + onRemoveButtonPressed: { + type: Function, + default: ()=>{} + } + }, } </script> diff --git a/src/components/QueuePositionControl.vue b/src/components/QueuePositionControl.vue new file mode 100644 index 0000000000000000000000000000000000000000..643896460930f194052218842c398d0199fb4c06 --- /dev/null +++ b/src/components/QueuePositionControl.vue @@ -0,0 +1,76 @@ +<template> + <div class="box" id="container"> + + <div> + <div v-if="queuePosition"> + <h2>You are currently {{queuePositionVerb}}:</h2> + <h3><i>{{ studentName }}</i></h3> + <h3>{{ queuePositionHelper }}</h3> + <h3><i>Assignment{{ assignmentNumber }}</i> </h3> + </div> + <div v-else> + <h3>Please pick a student from the queue to get started</h3> + </div> + + <div id="buttons"> + <button :disabled="!queuePosition || this.queuePosition.queue_type!=='Approval'">Approve</button> + <button class="" :disabled="!queuePosition">Postpone</button> + <button class="button-red" :disabled="!queuePosition" @click="removeCurrentSubjectQueueFromQueue">Remove</button> + </div> + </div> + </div> + +</template> + +<script> +import SubjectService from "@/service/SubjectService"; + +export default { + name: "QueuePositionControl", + props: { + queuePosition: { + type: Object, + required: true + } + }, + computed: { + queuePositionVerb() { + if(this.queuePosition.queue_type == "Approval") { return "approving"} + else if (this.queuePosition.queue_type == "Help") { return "helping"} + }, + queuePositionHelper() { + if(this.queuePosition.queue_type == "Approval") { return "for"} + else if (this.queuePosition.queue_type == "Help") { return "with"} + }, + studentName() { + return this.queuePosition.student.first_name + " " + this.queuePosition.student.surname; + }, + assignmentNumber() { + return this.queuePosition.assignment_number; + } + }, + methods: { + removeCurrentSubjectQueueFromQueue() { + SubjectService.deleteSubjectQueuePosition(this.queuePosition.subject_code, this.queuePosition.semester, this.queuePosition.student_user_id, this.$store.state.token) + .then(res => { + + }).catch(err => { + console.log(err) + }); + } + } +} +</script> + +<style scoped> +#container { + padding: 16px 16px 24px 16px; +} + +#buttons { + display: grid; + grid-auto-flow: column; + grid-column-gap: 16px; +} + +</style> diff --git a/src/components/SingleSubject.vue b/src/components/SingleSubject.vue index c8263bbe12006d2f07003018be4f5f8bb5471be1..2b81401022eacf34ebde85249629e4511ef2cf45 100644 --- a/src/components/SingleSubject.vue +++ b/src/components/SingleSubject.vue @@ -10,8 +10,8 @@ <div class="subjectOptions"> <button @click="pushToQueueScreen()">View Queue</button> - <button @click="chatClick">Chat</button> - <!--<button @click="">Åpne kø</button>--> + <button @click="chatClick" v-if="subject.active && type==='student'">Chat</button> + <button @click="" v-if="!subject.active && type==='studass'">Activate Queue</button> </div> </div> @@ -19,17 +19,29 @@ </template> <script> +import studAss from "@/components/StudAss"; + export default { props: { subject: { type: Object, required: true + }, + type: { + type: String, + validator(value) { + return['studass', 'student'].includes(value) + } } }, methods: { pushToQueueScreen() { - this.$router.push({name: 'Info', params: {subject_code: "IDATT2105", semester: "V22"}}) + if(this.type === 'studass') { + this.$router.push({name: 'StudentAssistantQueueView', params: {subject_code: this.subject.subject_code, semester: this.subject.semester }}) + } else if(this.type==='student') { + this.$router.push({name: 'StudentQueueView', params: {subject_code: this.subject.subject_code, semester: this.subject.semester }}) + } } } } diff --git a/src/components/StudAssAdminPanel.vue b/src/components/StudAssAdminPanel.vue new file mode 100644 index 0000000000000000000000000000000000000000..867cba524d54362a93a62fc7f96bcd0f8a7c9b6c --- /dev/null +++ b/src/components/StudAssAdminPanel.vue @@ -0,0 +1,79 @@ +<template> + <div class="studAddPanelContainer"> + + <h1>Welcome to the Student Assistant Admin Panel</h1> + <div class="activeQueuesContainer" v-if="this.getAvailableQueues"> + <h2>Your available queues:</h2> + <ul> + <li v-for="subject in this.getAvailableQueues" :key="subject.subject_code"> + <SingleSubject :subject="subject" type="studass"/> + </li> + </ul> + </div> + </div> + +</template> + +<script> + +import SubjectService from "@/service/SubjectService"; +import SingleSubject from "@/components/SingleSubject"; +import store from '@/store/index.js'; +import subjects from "@/views/Subjects"; + +export default { + components: {SingleSubject}, + + data() { + return { + subjects: null + } + }, + + computed: { + getAvailableQueues() { + console.log(subjects) + if(this.subjects != null) { + return this.subjects.filter(s => { + return !s.archived; + }) + } else { + return null; + } + } + }, + + mounted () { + return SubjectService.getStudentAssistantSubjectQueues(store.state.token) + .then(res => { + this.subjects = res.data; + }) + .catch(() => next({name: "Error"})) + }, + +} + +</script> + +<style> + +ul { + padding: 0; +} + +li { + list-style-type: none; + margin-bottom: 15px; +} + +.studAddPanelContainer { + max-width: 950px; + margin: 0 auto; +} + +.inactiveQueuesContainer { + width: 95%; + margin: 64px auto; +} + +</style> diff --git a/src/components/Student.vue b/src/components/Student.vue index 60bab551f078838ad1e8a8851da3a160eec7445e..b7630cbdcb71c609af3f2015f0c0962bd5572bc8 100644 --- a/src/components/Student.vue +++ b/src/components/Student.vue @@ -1,16 +1,48 @@ <template> - <div class="student" :class="{ studentActive: student.stud_ass }"> - <p>Navn: {{ student.student.first_name }}</p> - <p>Type: {{ student.queue_type }}</p> - <!-- <p>Stud.ass: {{ student.status }}</p> --> + <div class="student box" :class="{ studentActive: student.active_student_assistant_id != -1 }"> + <h4>{{ student.student.first_name + " " + student.student.surname }}</h4> + <p>{{ student.queue_type }}</p> + <p>Assignment 1</p> + <div> + <button v-if="showPickButton" @click="pickButtonPressed">Pick</button> + <button v-if="showRemoveButton" @click="removeButtonPressed">Remove</button> + </div> </div> </template> <script> export default { + props: { + student : { + required: true, + type: Object + }, + showPickButton: { + type: Boolean, + default: false + }, + showRemoveButton: { + type: Boolean, + default: false + }, + onPickButtonPressed: { + type: Function, + default: ()=>{} + }, + onRemoveButtonPressed: { + type: Function, + default: ()=>{} + } + }, - props: ['student'], - + methods: { + pickButtonPressed() { + this.onPickButtonPressed(this.student); + }, + removeButtonPressed() { + this.onRemoveButtonPressed(this.student); + } + } } </script> @@ -18,25 +50,18 @@ export default { <style scoped> .student { - margin: 20px auto; - background: white; - padding: 10px 20px; - border-radius: 4px; - box-shadow: 1px 2px 3px rgba(233, 7, 101, 0.05); - border-left: 4px solid #e90074; - display: flex; + margin: 20px auto; + background: white; + padding: 10px 20px 20px; + border-left: 6px solid #9f9f9f; + display: flex; flex-direction: row; justify-content: space-between; align-items: center; } - .studentActive { - - border-left: 4px solid #00e94e; - - - + border-left: 8px solid #00e94e; } </style> diff --git a/src/components/SubjectList.vue b/src/components/SubjectList.vue index 77ac71a322790ac715a5aa1bf750004f2b9b7230..d33aca94cbe4ac9780b9bebabbbb19222b2ba675 100644 --- a/src/components/SubjectList.vue +++ b/src/components/SubjectList.vue @@ -2,19 +2,19 @@ <div class="container"> <div class="activeQueuesContainer" v-if="this.getActiveSubjects"> - <h1>Active Queues</h1> + <h1 class="underline">Active Queues</h1> <ul> <li v-for="subject in this.getActiveSubjects" :key="subject.subject_code"> - <SingleSubject :subject="subject" /> + <SingleSubject :subject="subject" type="student" /> </li> </ul> </div> <div class="inactiveQueuesContainer" v-if="this.getInactiveSubjects"> - <h1>Inactive Queues</h1> + <h2>Inactive Queues</h2> <ul> <li v-for="subject in this.getInactiveSubjects" :key="subject.subject_code"> - <SingleSubject :subject="subject" /> + <SingleSubject :subject="subject" type="student"/> </li> </ul> </div> @@ -58,8 +58,6 @@ export default { }, mounted () { - console.log("Before load") - return SubjectService.getStudentAssistantSubjectQueues(store.state.token) .then(res => { console.log(res.data) @@ -90,11 +88,9 @@ li { } .inactiveQueuesContainer { - margin-top: 84px; + width: 95%; + margin: 64px auto; } -.container h1 { - text-decoration: underline; -} </style> diff --git a/src/router/index.js b/src/router/index.js index 6495713ad7448553a3fb3d03ca9ba8104e96bb18..6ae29e21ee00fbf8e93dabff7ee795e8f939e49f 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -3,7 +3,8 @@ import Login from '../views/Login.vue' 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 StudentQueueView from '../views/StudentQueueView.vue' +import StudentAssistantQueueView from "@/views/StudentAssistantQueueView"; import store from "@/store"; const routes = [ @@ -13,7 +14,7 @@ const routes = [ component: Login }, { - path: '/Settings', + path: '/settings', name: 'Settings', component: Settings, meta: { @@ -21,23 +22,31 @@ const routes = [ } }, { - path: '/Subjects/:tab', + path: '/subjects/:tab', name: 'Subjects', component: Subjects, meta: { requiresAuth: true } }, - { //path: '/Subjects/Exercises/:subject_id', // dynamic path - path: '/Subjects/:subject_code/:semester/Info', - name: 'Info', - component: Info, + path: '/subjects/student/:subject_code/:semester/queue', + name: 'StudentQueueView', + component: StudentQueueView, meta: { requiresAuth: true } }, + { + path: '/subjects/studentassistant/:subject_code/:semester/queue', + name: 'StudentAssistantQueueView', + component: StudentAssistantQueueView, + meta: { + requiresAuth: true + } + }, + // Redirect any none-existing path to 404 page { path: '/:catchAll(.*)', diff --git a/src/service/AssignmentService.js b/src/service/AssignmentService.js index 6b7f733d72d6b7d3dda35996cc7285e64a7d9e02..6394feaf30ce42618a90141f0b127a01108cee30 100644 --- a/src/service/AssignmentService.js +++ b/src/service/AssignmentService.js @@ -11,11 +11,11 @@ export default { * @returns {Promise<AxiosResponse<any>>} */ getAssignmentsForStudent(subject, token) { - + const subject_code = subject.subjectInfo.subject_code; const semester = subject.semester; - - + + return apiService.getApiClient(token).get(`/subjects/assignments/${subject_code}/${semester}`) } diff --git a/src/service/SubjectService.js b/src/service/SubjectService.js index e3f4c2a7b8de54fe8703b9240535d6c23ee77aac..606a2217824c200ae23f877b8290ee793146d8d2 100644 --- a/src/service/SubjectService.js +++ b/src/service/SubjectService.js @@ -65,5 +65,28 @@ export default { */ getSubject(subject_code, semester, token) { return apiService.getApiClient(token).get(`/subjects/${subject_code}/${semester}`) + }, + + /** + * Deletes a queue position from the database + * + * @param subject_code + * @param semester + * @param student_id + * @param token + * @returns {Promise<AxiosResponse<any>>} + */ + deleteSubjectQueuePosition(subject_code, semester, student_id, token) { + return apiService.getApiClient(token).delete(`/subjects/queues/${subject_code}/${semester}/positions/${student_id}`) + }, + + /** + * Update an existing queue position with new data. + * + * @param queuePosition + * @param token + */ + updateQueuePosition(queuePosition, token) { + return apiService.getApiClient(token).post("/subjects/queues/positions", queuePosition) } } diff --git a/src/service/UserAccountService.js b/src/service/UserAccountService.js index f1ff8de81f6cc159d4136e1f76155350ca981d50..9e8109c087747ef25659282ba92d9ce096cc910a 100644 --- a/src/service/UserAccountService.js +++ b/src/service/UserAccountService.js @@ -9,6 +9,15 @@ export default { */ getRolesForLoggedInUser(token) { return apiService.getApiClient(token).get('/user/roles/'); - } + }, + /** + * Gets the user details of the user associated with the given email + * + * @param email + * @returns {Promise<AxiosResponse<any>>} + */ + getUserAccountDetails(email, token) { + return apiService.getApiClient(token).get(`/user/${email}`); + } } diff --git a/src/store/index.js b/src/store/index.js index 968a515b6645f83d1a40505a2f876b91f4e93a12..11b419f6364499749c80b8421c5444ae339328bc 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -5,15 +5,14 @@ export default createStore({ state: { // variables accessable from anywhere within the app currentView:'', // den her Email: '', // den her + currentUserDetails: '', + userRoles: null, token: '', loggedIn: false, // den her forgotPassword: false, message: '', - currentOption: 'Student', // den her currentSubject: null, subjects: [], - - }, mutations: { // methods to change variables within the state @@ -28,10 +27,8 @@ export default createStore({ setToken(state, newToken) { state.token = newToken }, - setLogin(state, newEmail, newPassword) { + setLogin(state, newEmail) { state.Email = newEmail - state.password = newPassword - state.currentOption = 'Student' if(state.token == '') { state.loggedIn = false } @@ -39,6 +36,12 @@ export default createStore({ state.loggedIn = true } }, + setUserAccountDetails(state, accDetails) { + state.currentUserDetails = accDetails; + }, + setActiveRoles(state, roles) { + state.userRoles = roles; + }, forgotPassword(state) { state.forgotPassword = !state.forgotPassword diff --git a/src/views/StudentAssistantQueueView.vue b/src/views/StudentAssistantQueueView.vue new file mode 100644 index 0000000000000000000000000000000000000000..aa7df06ce0092215d1225c4128e7c8df87ad91c7 --- /dev/null +++ b/src/views/StudentAssistantQueueView.vue @@ -0,0 +1,131 @@ +<template> + <h1>Student Assistant Queue-Panel</h1> + <h2>{{subject_name}}</h2> + + <div class="StudentAssistantQueueViewContainer"> + + <div> + <h2>Queue Control</h2> + <QueuePositionControl :queue-position="currentQueuePosition" /> + </div> + + <div class="queue"> + <h2>Queue</h2> + <Queue :studentQueue="queuePositions" show-tool-buttons :on-pick-button-pressed="onPickPressed" /> + </div> + </div> + +</template> + +<script> +import SubjectQueueService from "@/service/SubjectService"; +import Queue from "@/components/Queue"; +import QueuePositionControl from "@/components/QueuePositionControl"; + +export default { + name: "StudentAssistantQueueInfo.vue", + components: {QueuePositionControl, Queue}, + + data() { + return { + subject: null, + queuePositions: null, + subject_name: null, + currentQueuePosition: null, + ownUserId: null + } + }, + + async mounted() { + this.ownUserId = this.$store.state.currentUserDetails.user_id; + + SubjectQueueService.getSubject(this.getSubjectCode, this.getSemester, this.$store.state.token). + then(ans => { + this.subject_name = ans.data.subjectInfo.name + this.subject = ans.data + + this.timer = setInterval(this.updateQueueData, 500); + }) + }, + + computed: { + getSubjectCode() { + return this.$route.params.subject_code + }, + + getSemester() { + return this.$route.params.semester + } + }, + + methods: { + updateQueueData() { + SubjectQueueService.getSubjectQueuePositions(this.subject, this.$store.state.token). + then(ans => { + this.queuePositions = ans.data; + + if(this.queuePositions==null||this.queuePositions.length === 0) { + this.currentQueuePosition = null; + } + + for(let qp of this.queuePositions) { + const currentlyHelping = qp.active_student_assistant_id === this.ownUserId; + + if(currentlyHelping) { + this.currentQueuePosition = qp; + break; + } else { + this.currentQueuePosition = null + } + } + + this.queuePositions.forEach(qp => { + const currentlyHelping = qp.active_student_assistant_id === this.ownUserId; + + if(currentlyHelping) { + this.currentQueuePosition = qp; + } + + }) + }).catch(err => { + console.log(err) + }) + }, + onPickPressed(queuePosition) { + console.log(queuePosition) + queuePosition.active_student_assistant_id = this.ownUserId; + SubjectQueueService.updateQueuePosition(queuePosition, this.$store.state.token).then(() => { + this.updateQueueData(); + }).catch(err => { + console.log(err) + }) + }, + onRemovePressed() { + + } + } + +} +</script> + +<style scoped> + +.StudentAssistantQueueViewContainer { + margin: auto; + max-width: 1512px; + display: grid; + grid-template-columns: 50% 50%; + grid-column-gap: 24px; +} + +@media only screen and (max-width: 768px) { + + .StudentAssistantQueueViewContainer { + grid-auto-flow: row; + grid-column-gap: 10px; + + } + +} + +</style> diff --git a/src/views/Info.vue b/src/views/StudentQueueView.vue similarity index 99% rename from src/views/Info.vue rename to src/views/StudentQueueView.vue index 455541f1731587ec2c7912bd74910c8e09ff0e84..482b96aecab53c4b2f43e4cb6c622a6b315ea023 100644 --- a/src/views/Info.vue +++ b/src/views/StudentQueueView.vue @@ -14,8 +14,6 @@ </div> - - </template> <script> @@ -43,7 +41,6 @@ export default { }, mounted() { - SubjectQueueService.getSubject(this.getSubjectCode, this.getSemester, this.$store.state.token). then(ans => { @@ -70,15 +67,11 @@ export default { }).catch(err => console.log(err)) - - - }, computed: { - getSubjectCode() { return this.$route.params.subject_code diff --git a/src/views/Subjects.vue b/src/views/Subjects.vue index 0e9cd048057382e02cb99ea47cee5a556b8d6651..52ba789319d64f44ba916b31cd95b929eeea339b 100644 --- a/src/views/Subjects.vue +++ b/src/views/Subjects.vue @@ -1,7 +1,10 @@ <template> <OptionBar :tab="getActiveTab" /> - <SubjectList /> + + <SubjectList v-if="getActiveTab==='student'"/> + <StudAssAdminPanel v-if="getActiveTab==='studentassistant'"/> + </template> @@ -12,10 +15,11 @@ import OptionBar from '../components/OptionBar.vue' import SubjectQueueService from '../service/SubjectService.js' import SubjectList from '../components/SubjectList.vue' +import StudAssAdminPanel from "@/components/StudAssAdminPanel"; export default { name: 'Subjects', - components: {OptionBar, SubjectList}, + components: {OptionBar, SubjectList, StudAssAdminPanel}, data() { return {