Add user profile page
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-navigation-drawer v-if="$store.state.user" app dark clipped :mini-variant="miniVariant">
|
<v-navigation-drawer v-if="$store.state.user" app dark clipped :mini-variant="miniVariant">
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item link class="px-2" @click="gotoProfilePage">
|
<v-list-item link class="px-2" :to="{ name: 'user' }">
|
||||||
<v-list-item-avatar>
|
<v-list-item-avatar>
|
||||||
<v-img src="https://avatarfiles.alphacoders.com/207/207426.png"></v-img>
|
<v-img src="https://avatarfiles.alphacoders.com/207/207426.png"></v-img>
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title>{{ userName }}</v-list-item-title>
|
<v-list-item-title>{{ $store.state.user.displayName }}</v-list-item-title>
|
||||||
<v-list-item-subtitle>{{ $auth.currentUser.email }}</v-list-item-subtitle>
|
<v-list-item-subtitle>{{ $auth.currentUser.email }}</v-list-item-subtitle>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
@@ -30,27 +30,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _capitalize from 'lodash-es/capitalize'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
miniVariant: false
|
miniVariant: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
userName () {
|
|
||||||
const givenName = this.$auth.currentUser.email.split('.')[0]
|
|
||||||
return _capitalize(givenName)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
toggle () {
|
toggle () {
|
||||||
this.miniVariant = !this.miniVariant
|
this.miniVariant = !this.miniVariant
|
||||||
},
|
|
||||||
gotoProfilePage () {
|
|
||||||
// TODO
|
|
||||||
this.$toast({ content: 'TODO: Benutzerbereich implementieren', color: 'info' })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import {
|
|||||||
} from 'firebase/firestore'
|
} from 'firebase/firestore'
|
||||||
import _sampleSize from 'lodash-es/sampleSize'
|
import _sampleSize from 'lodash-es/sampleSize'
|
||||||
import _shuffle from 'lodash-es/shuffle'
|
import _shuffle from 'lodash-es/shuffle'
|
||||||
import _capitalize from 'lodash-es/capitalize'
|
|
||||||
import { ClosedEndedQuestionConverter, states } from '~/plugins/closed-ended-question'
|
import { ClosedEndedQuestionConverter, states } from '~/plugins/closed-ended-question'
|
||||||
import { Game, GameConverter } from '~/plugins/game'
|
import { Game, GameConverter } from '~/plugins/game'
|
||||||
|
|
||||||
@@ -92,9 +91,6 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
generateUserName () {
|
|
||||||
return _capitalize(this.$auth.currentUser.email.split('.')[0])
|
|
||||||
},
|
|
||||||
getQuestions () {
|
getQuestions () {
|
||||||
// Create a reference to the closed-ended questions collection of the currently selected course
|
// Create a reference to the closed-ended questions collection of the currently selected course
|
||||||
const questionsRef = collection(this.$db, `kurse/${this.courseID}/fragenGeschlossen`).withConverter(ClosedEndedQuestionConverter)
|
const questionsRef = collection(this.$db, `kurse/${this.courseID}/fragenGeschlossen`).withConverter(ClosedEndedQuestionConverter)
|
||||||
@@ -167,7 +163,7 @@ export default {
|
|||||||
joinGame (game) {
|
joinGame (game) {
|
||||||
this.game = game
|
this.game = game
|
||||||
this.game.player2id = this.$auth.currentUser.uid
|
this.game.player2id = this.$auth.currentUser.uid
|
||||||
this.game.player2name = this.generateUserName()
|
this.game.player2name = this.$store.state.user.displayName
|
||||||
this.game.player2answers = []
|
this.game.player2answers = []
|
||||||
this.selectedQuestions = this.questions.filter(q => game.questions.includes(q.id))
|
this.selectedQuestions = this.questions.filter(q => game.questions.includes(q.id))
|
||||||
|
|
||||||
@@ -205,7 +201,7 @@ export default {
|
|||||||
this.selectedQuestions.map(q => q.id),
|
this.selectedQuestions.map(q => q.id),
|
||||||
Date.now() / 1000, // Current UNIX timestamp in seconds
|
Date.now() / 1000, // Current UNIX timestamp in seconds
|
||||||
this.$auth.currentUser.uid,
|
this.$auth.currentUser.uid,
|
||||||
this.generateUserName(),
|
this.$store.state.user.displayName,
|
||||||
[],
|
[],
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onAuthStateChanged } from 'firebase/auth'
|
import { onAuthStateChanged } from 'firebase/auth'
|
||||||
import { doc, getDoc, setDoc } from 'firebase/firestore'
|
import { doc, getDoc, setDoc } from 'firebase/firestore'
|
||||||
|
import _capitalize from 'lodash-es/capitalize'
|
||||||
import { User, UserConverter } from '~/plugins/user'
|
import { User, UserConverter } from '~/plugins/user'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -51,7 +52,8 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
createUser () {
|
createUser () {
|
||||||
const user = new User()
|
const displayName = _capitalize(this.$auth.currentUser.email.split('.')[0])
|
||||||
|
const user = new User(displayName, [])
|
||||||
|
|
||||||
// Add a new document in collection "users"
|
// Add a new document in collection "users"
|
||||||
setDoc(doc(this.$db, 'benutzer', this.$auth.currentUser.uid).withConverter(UserConverter), user)
|
setDoc(doc(this.$db, 'benutzer', this.$auth.currentUser.uid).withConverter(UserConverter), user)
|
||||||
|
|||||||
119
pages/user.vue
Normal file
119
pages/user.vue
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<template>
|
||||||
|
<v-container>
|
||||||
|
<v-sheet class="rounded-lg pa-4">
|
||||||
|
<v-row align="center">
|
||||||
|
<v-col cols="auto" style="position: relative">
|
||||||
|
<v-avatar size="200">
|
||||||
|
<v-img src="https://avatarfiles.alphacoders.com/207/207426.png"></v-img>
|
||||||
|
</v-avatar>
|
||||||
|
<v-btn fab absolute dark style="bottom: 15px; right: 15px" @click="changeProfilePic">
|
||||||
|
<v-icon>mdi-account-edit</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col>
|
||||||
|
<div class="text-h4">{{ userFullName }}</div>
|
||||||
|
<div class="text-h6 text--secondary">{{ userRole }}</div>
|
||||||
|
<v-spacer class="my-6"></v-spacer>
|
||||||
|
<div class="text-subtitle-1 text--secondary">Registriert: {{ formatDate($auth.currentUser.metadata.creationTime) }}</div>
|
||||||
|
<div class="text-subtitle-1 text--secondary">Letzter Login: {{ formatDate($auth.currentUser.metadata.lastSignInTime) }}</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-divider></v-divider>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field v-model="displayName" label="Anzeigename" counter="15" :maxlength="15"></v-text-field>
|
||||||
|
<v-text-field v-model="fullName" label="Vollständiger Name" disabled></v-text-field>
|
||||||
|
<v-text-field v-model="email" label="E-Mail-Adresse" disabled></v-text-field>
|
||||||
|
<v-btn depressed color="primary" :loading="loading" @click="save">
|
||||||
|
Änderungen speichern
|
||||||
|
</v-btn>
|
||||||
|
<v-btn outlined color="primary" @click="logout">
|
||||||
|
<v-icon left>mdi-logout-variant</v-icon>
|
||||||
|
Logout
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-sheet>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { signOut } from 'firebase/auth'
|
||||||
|
import { doc, updateDoc } from 'firebase/firestore'
|
||||||
|
import _capitalize from 'lodash-es/capitalize'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UserPage',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
fullName: '',
|
||||||
|
displayName: '',
|
||||||
|
email: '',
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
userFullName () {
|
||||||
|
const nameParsed = this.$auth.currentUser.email.split('@')[0]
|
||||||
|
const nameParts = []
|
||||||
|
|
||||||
|
for (const namePart of nameParsed.split('.')) {
|
||||||
|
nameParts.push(_capitalize(namePart))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameParts.join(' ')
|
||||||
|
},
|
||||||
|
userRole () {
|
||||||
|
const isTutor = this.$auth.currentUser.email.endsWith('@iu.org')
|
||||||
|
const isAdmin = this.$store.getters.isAdmin
|
||||||
|
if (isAdmin) return 'Admin'
|
||||||
|
else if (isTutor) return 'Tutor'
|
||||||
|
else return 'Student'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.fullName = this.userFullName
|
||||||
|
this.displayName = this.$store.state.user.displayName
|
||||||
|
this.email = this.$auth.currentUser.email
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatDate (str) {
|
||||||
|
return new Date(str).toLocaleString('de-DE')
|
||||||
|
},
|
||||||
|
changeProfilePic () {
|
||||||
|
// TODO
|
||||||
|
this.$toast({ content: 'Diese Funktion ist in der Demo-Version nicht verfügbar.', color: 'info', timeout: 3000 })
|
||||||
|
},
|
||||||
|
save () {
|
||||||
|
const newDisplayName = this.displayName.trim()
|
||||||
|
if (!newDisplayName || newDisplayName === this.$store.state.user.displayName) return
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
const ref = doc(this.$db, `benutzer/${this.$auth.currentUser.uid}`)
|
||||||
|
updateDoc(ref, {
|
||||||
|
anzeigename: newDisplayName
|
||||||
|
}).then((empty) => {
|
||||||
|
this.$store.commit('setUserDisplayName', newDisplayName)
|
||||||
|
this.$toast({ content: 'Die Änderungen wurden gespeichert!', color: 'success' })
|
||||||
|
}).catch((error) => {
|
||||||
|
// Failed to update user profile; display error message
|
||||||
|
this.$toast({content: error, color: 'error'})
|
||||||
|
}).then(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
logout () {
|
||||||
|
signOut(this.$auth).then(() => {
|
||||||
|
// Sign-out successful
|
||||||
|
// The authentication state observer will redirect the user to the main page (dashboard),
|
||||||
|
// see pages/index.vue
|
||||||
|
}).catch((error) => {
|
||||||
|
// An error happened.
|
||||||
|
this.$toast({ content: error, color: 'error' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export class User {
|
export class User {
|
||||||
constructor (gamesStarted) {
|
constructor (displayName, gamesStarted) {
|
||||||
|
this.displayName = displayName
|
||||||
this.gamesStarted = []
|
this.gamesStarted = []
|
||||||
|
|
||||||
if (gamesStarted && gamesStarted.length > 0) {
|
if (gamesStarted && gamesStarted.length > 0) {
|
||||||
@@ -19,11 +20,12 @@ export const UserConverter = {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
anzeigename: user.displayName,
|
||||||
spieleBegonnen
|
spieleBegonnen
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fromFirestore: (snapshot, options) => {
|
fromFirestore: (snapshot, options) => {
|
||||||
const data = snapshot.data(options)
|
const data = snapshot.data(options)
|
||||||
return new User(data.spieleBegonnen)
|
return new User(data.anzeigename, data.spieleBegonnen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,9 @@ export const mutations = {
|
|||||||
setUser (state, user) {
|
setUser (state, user) {
|
||||||
state.user = user
|
state.user = user
|
||||||
},
|
},
|
||||||
|
setUserDisplayName (state, name) {
|
||||||
|
this._vm.$set(state.user, 'displayName', name)
|
||||||
|
},
|
||||||
setCourses (state, courses) {
|
setCourses (state, courses) {
|
||||||
state.courses = courses
|
state.courses = courses
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user