Add user profile page

This commit is contained in:
2022-11-03 18:13:05 +01:00
parent a2f0221229
commit 9384ece657
6 changed files with 133 additions and 23 deletions

View File

@@ -1,12 +1,12 @@
<template>
<v-navigation-drawer v-if="$store.state.user" app dark clipped :mini-variant="miniVariant">
<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-img src="https://avatarfiles.alphacoders.com/207/207426.png"></v-img>
</v-list-item-avatar>
<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-content>
</v-list-item>
@@ -30,27 +30,15 @@
</template>
<script>
import _capitalize from 'lodash-es/capitalize'
export default {
data () {
return {
miniVariant: false
}
},
computed: {
userName () {
const givenName = this.$auth.currentUser.email.split('.')[0]
return _capitalize(givenName)
}
},
methods: {
toggle () {
this.miniVariant = !this.miniVariant
},
gotoProfilePage () {
// TODO
this.$toast({ content: 'TODO: Benutzerbereich implementieren', color: 'info' })
}
}
}

View File

@@ -40,7 +40,6 @@ import {
} from 'firebase/firestore'
import _sampleSize from 'lodash-es/sampleSize'
import _shuffle from 'lodash-es/shuffle'
import _capitalize from 'lodash-es/capitalize'
import { ClosedEndedQuestionConverter, states } from '~/plugins/closed-ended-question'
import { Game, GameConverter } from '~/plugins/game'
@@ -92,9 +91,6 @@ export default {
})
},
methods: {
generateUserName () {
return _capitalize(this.$auth.currentUser.email.split('.')[0])
},
getQuestions () {
// 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)
@@ -167,7 +163,7 @@ export default {
joinGame (game) {
this.game = game
this.game.player2id = this.$auth.currentUser.uid
this.game.player2name = this.generateUserName()
this.game.player2name = this.$store.state.user.displayName
this.game.player2answers = []
this.selectedQuestions = this.questions.filter(q => game.questions.includes(q.id))
@@ -205,7 +201,7 @@ export default {
this.selectedQuestions.map(q => q.id),
Date.now() / 1000, // Current UNIX timestamp in seconds
this.$auth.currentUser.uid,
this.generateUserName(),
this.$store.state.user.displayName,
[],
'',
'',

View File

@@ -7,6 +7,7 @@
<script>
import { onAuthStateChanged } from 'firebase/auth'
import { doc, getDoc, setDoc } from 'firebase/firestore'
import _capitalize from 'lodash-es/capitalize'
import { User, UserConverter } from '~/plugins/user'
export default {
@@ -51,7 +52,8 @@ export default {
})
},
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"
setDoc(doc(this.$db, 'benutzer', this.$auth.currentUser.uid).withConverter(UserConverter), user)

119
pages/user.vue Normal file
View 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>

View File

@@ -1,5 +1,6 @@
export class User {
constructor (gamesStarted) {
constructor (displayName, gamesStarted) {
this.displayName = displayName
this.gamesStarted = []
if (gamesStarted && gamesStarted.length > 0) {
@@ -19,11 +20,12 @@ export const UserConverter = {
})
return {
anzeigename: user.displayName,
spieleBegonnen
}
},
fromFirestore: (snapshot, options) => {
const data = snapshot.data(options)
return new User(data.spieleBegonnen)
return new User(data.anzeigename, data.spieleBegonnen)
}
}

View File

@@ -25,6 +25,9 @@ export const mutations = {
setUser (state, user) {
state.user = user
},
setUserDisplayName (state, name) {
this._vm.$set(state.user, 'displayName', name)
},
setCourses (state, courses) {
state.courses = courses
},