Add option for admins to create new courses
This commit is contained in:
123
components/AddCourse.vue
Normal file
123
components/AddCourse.vue
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog v-model="show" max-width="500" persistent>
|
||||||
|
<template #activator="{ on, attrs }">
|
||||||
|
<v-card flat width="100%" class="rounded-lg" v-bind="attrs" v-on="on">
|
||||||
|
<v-card-text class="text-h6">
|
||||||
|
<v-icon left x-large>mdi-plus</v-icon>
|
||||||
|
Kurs hinzufügen
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
<ValidationObserver ref="addCourseObserver" v-slot="{ invalid }">
|
||||||
|
<form @submit.prevent="addCourse">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>Neuer Kurs</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<ValidationProvider v-slot="{ errors }" name="courseID" rules="required">
|
||||||
|
<v-text-field
|
||||||
|
v-model="id"
|
||||||
|
required
|
||||||
|
label="ID"
|
||||||
|
persistent-placeholder
|
||||||
|
placeholder="ISEF01"
|
||||||
|
:error-messages="errors"
|
||||||
|
></v-text-field>
|
||||||
|
</ValidationProvider>
|
||||||
|
<ValidationProvider v-slot="{ errors }" name="course" rules="required">
|
||||||
|
<v-text-field
|
||||||
|
v-model="title"
|
||||||
|
required
|
||||||
|
label="Bezeichnung"
|
||||||
|
persistent-placeholder
|
||||||
|
placeholder="Projekt Software Engineering"
|
||||||
|
:error-messages="errors"
|
||||||
|
></v-text-field>
|
||||||
|
</ValidationProvider>
|
||||||
|
<ValidationProvider v-slot="{ errors }" name="tutor" rules="required|email">
|
||||||
|
<v-text-field
|
||||||
|
v-model="tutor"
|
||||||
|
required
|
||||||
|
label="Tutor E-Mail"
|
||||||
|
persistent-placeholder
|
||||||
|
placeholder="john.johnson@iu.org"
|
||||||
|
:error-messages="errors"
|
||||||
|
></v-text-field>
|
||||||
|
</ValidationProvider>
|
||||||
|
</v-card-text>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn text color="primary" @click="close">Abbrechen</v-btn>
|
||||||
|
<v-btn type="submit" depressed color="primary" :loading="loading" :disabled="invalid">Hinzufügen</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</form>
|
||||||
|
</ValidationObserver>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { doc, setDoc } from 'firebase/firestore'
|
||||||
|
// We use vee-validate@3 for form validation.
|
||||||
|
// https://vee-validate.logaretm.com/v3/guide/basics.html
|
||||||
|
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate'
|
||||||
|
import { required, email } from 'vee-validate/dist/rules'
|
||||||
|
|
||||||
|
// Override the default error message of required fields
|
||||||
|
extend('required', {
|
||||||
|
...required,
|
||||||
|
message: 'Pflichtfeld'
|
||||||
|
})
|
||||||
|
|
||||||
|
extend('email', {
|
||||||
|
...email,
|
||||||
|
message: 'Keine gültige E-Mail-Adresse'
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AddCourseDialog',
|
||||||
|
components: {
|
||||||
|
ValidationProvider,
|
||||||
|
ValidationObserver
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
show: false,
|
||||||
|
loading: false,
|
||||||
|
id: '',
|
||||||
|
title: '',
|
||||||
|
tutor: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addCourse () {
|
||||||
|
this.loading = true
|
||||||
|
const courseID = this.id.toLowerCase().trim()
|
||||||
|
const courseName = this.title.trim()
|
||||||
|
const tutorEmail = this.tutor.toLowerCase().trim()
|
||||||
|
const c = { name: courseName, tutor: tutorEmail }
|
||||||
|
|
||||||
|
// Add a new document
|
||||||
|
setDoc(doc(this.$db, `kurse/${courseID}`), c)
|
||||||
|
.then((docRef) => {
|
||||||
|
// Successfully added a new course to the database.
|
||||||
|
this.$store.commit('setCourse', { courseID, courseData: c })
|
||||||
|
this.$toast({content: `Der Kurs "${courseID.toUpperCase()} - ${courseName}" wurde hinzugefügt!`, color: 'success'})
|
||||||
|
this.close()
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// Failed to add a new course to the database; display error message
|
||||||
|
this.$toast({content: error, color: 'error'})
|
||||||
|
})
|
||||||
|
.then(() => { this.loading = false })
|
||||||
|
},
|
||||||
|
close () {
|
||||||
|
this.id = ''
|
||||||
|
this.title = ''
|
||||||
|
this.tutor = ''
|
||||||
|
this.show = false
|
||||||
|
this.$refs.addCourseObserver.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container fluid>
|
<v-container fluid>
|
||||||
<v-row>
|
<v-row>
|
||||||
|
<v-col v-if="$store.getters.isAdmin" cols="12">
|
||||||
|
<AddCourse />
|
||||||
|
</v-col>
|
||||||
<v-col v-for="(course, id) in courses" :key="id" cols="12" lg="6">
|
<v-col v-for="(course, id) in courses" :key="id" cols="12" lg="6">
|
||||||
<v-card flat width="100%" class="rounded-lg" @click="openCourse(id)">
|
<v-card flat width="100%" class="rounded-lg" @click="openCourse(id)">
|
||||||
<v-row no-gutters class="flex-nowrap">
|
<v-row no-gutters class="flex-nowrap">
|
||||||
@@ -26,16 +29,12 @@
|
|||||||
<script>
|
<script>
|
||||||
import _cloneDeep from 'lodash-es/cloneDeep'
|
import _cloneDeep from 'lodash-es/cloneDeep'
|
||||||
import { collection, getDocs } from 'firebase/firestore'
|
import { collection, getDocs } from 'firebase/firestore'
|
||||||
import { demoAccounts } from '~/components/DemoInfoDialog.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DashboardPage',
|
name: 'DashboardPage',
|
||||||
layout ({ $auth }) {
|
layout ({ $auth }) {
|
||||||
// Ref: https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#currentuser
|
// Ref: https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#currentuser
|
||||||
// return $auth.currentUser.emailVerified ? 'default' : 'unverified'
|
return $auth.currentUser.emailVerified ? 'default' : 'unverified'
|
||||||
|
|
||||||
// TODO: For demo purposes only. Delete in production.
|
|
||||||
return $auth.currentUser.emailVerified || demoAccounts.includes($auth.currentUser.email) ? 'default' : 'unverified'
|
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
export const state = () => ({
|
export const state = () => ({
|
||||||
firebaseInitialized: false,
|
firebaseInitialized: false,
|
||||||
|
idTokenResult: null,
|
||||||
user: null,
|
user: null,
|
||||||
courses: {},
|
courses: {},
|
||||||
selectedCourse: undefined
|
selectedCourse: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
|
isAdmin (state) {
|
||||||
|
return state.idTokenResult && state.idTokenResult.claims.admin
|
||||||
|
},
|
||||||
getCourseByID: (state) => (courseID) => {
|
getCourseByID: (state) => (courseID) => {
|
||||||
return state.courses[courseID]
|
return state.courses[courseID]
|
||||||
}
|
}
|
||||||
@@ -15,12 +19,18 @@ export const mutations = {
|
|||||||
initFirebase (state) {
|
initFirebase (state) {
|
||||||
state.firebaseInitialized = true
|
state.firebaseInitialized = true
|
||||||
},
|
},
|
||||||
|
setIdTokenResult (state, idTokenResult) {
|
||||||
|
state.idTokenResult = idTokenResult
|
||||||
|
},
|
||||||
setUser (state, user) {
|
setUser (state, user) {
|
||||||
state.user = user
|
state.user = user
|
||||||
},
|
},
|
||||||
setCourses (state, courses) {
|
setCourses (state, courses) {
|
||||||
state.courses = courses
|
state.courses = courses
|
||||||
},
|
},
|
||||||
|
setCourse (state, { courseID, courseData }) {
|
||||||
|
this._vm.$set(state.courses, courseID, courseData)
|
||||||
|
},
|
||||||
setSelectedCourse (state, courseID) {
|
setSelectedCourse (state, courseID) {
|
||||||
state.selectedCourse = courseID
|
state.selectedCourse = courseID
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user