large backend recreation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
b8e45663e1
commit
6fb439a754
199
frontend/app.vue
199
frontend/app.vue
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="!userLoaded" class="bg-neutral-100 dark:bg-neutral-900 text-primary h-screen flex justify-center items-center">
|
<div v-if="!userStore.userLoaded" class="bg-neutral-100 dark:bg-neutral-900 text-primary h-screen flex justify-center items-center">
|
||||||
<div>
|
<div>
|
||||||
<Icon size="4em" name="ion:load-c" class="animate-spin" />
|
<Icon size="4em" name="ion:load-c" class="animate-spin" />
|
||||||
<h2 class="mt-2 font-bold">Loading...</h2>
|
<h2 class="mt-2 font-bold">Loading...</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="">
|
<div v-else class="">
|
||||||
<div v-if="isAuthenticated" class="bg-neutral-100 dark:bg-neutral-900 text-primary h-screen flex flex-col">
|
<div v-if="userStore.isAuthenticated" class="bg-neutral-100 dark:bg-neutral-900 text-primary h-screen flex flex-col">
|
||||||
<LayoutTopbar />
|
<LayoutTopbar />
|
||||||
<div class="overflow-y-auto pt-3">
|
<div class="overflow-y-auto pt-3">
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
@ -20,35 +20,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { doc, getFirestore, serverTimestamp, writeBatch, collection, getDoc } from "firebase/firestore";
|
|
||||||
import { getAuth, onAuthStateChanged, signInWithEmailAndPassword } from "firebase/auth";
|
|
||||||
import { getMessaging, getToken, onMessage } from "firebase/messaging";
|
|
||||||
import { PushNotifications } from '@capacitor/push-notifications';
|
|
||||||
import { Device } from '@capacitor/device';
|
import { Device } from '@capacitor/device';
|
||||||
import { useToast } from 'vue-toastification'
|
|
||||||
|
|
||||||
const db = getFirestore()
|
const userStore = useUserStore()
|
||||||
const route = useRoute()
|
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
const isAuthenticated = ref(false)
|
onMounted(async () => {
|
||||||
const user = ref('frikandel')
|
userStore.init()
|
||||||
const registrationToken = ref('')
|
|
||||||
const auth = ref(null)
|
|
||||||
const userLoaded = ref(false)
|
|
||||||
const userData = ref(null)
|
|
||||||
const userPersons = ref([])
|
|
||||||
const userAllPersons = ref([])
|
|
||||||
const calEvents = ref([])
|
|
||||||
const news = ref(null)
|
|
||||||
const contestTimes = ref(null)
|
|
||||||
const competitors = ref([])
|
|
||||||
const users = ref([])
|
|
||||||
|
|
||||||
const messaging = ref(null)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
auth.value = getAuth()
|
|
||||||
|
|
||||||
Device.getInfo().then(info => {
|
Device.getInfo().then(info => {
|
||||||
if (info.platform === 'ios') document.getElementsByClassName('top-right')[0].classList.add('toastios')
|
if (info.platform === 'ios') document.getElementsByClassName('top-right')[0].classList.add('toastios')
|
||||||
@ -57,176 +34,12 @@ onMounted(() => {
|
|||||||
if (process.client) {
|
if (process.client) {
|
||||||
if ('serviceWorker' in navigator && window.isSecureContext) {
|
if ('serviceWorker' in navigator && window.isSecureContext) {
|
||||||
Device.getInfo().then(info => {
|
Device.getInfo().then(info => {
|
||||||
if (info.platform === 'web') registerSW()
|
if (info.platform === 'web') registerServiceWorker()
|
||||||
else document.getElementsByClassName('top-right')[0].classList.add('toastios')
|
else document.getElementsByClassName('top-right')[0].classList.add('toastios')
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAuthStateChanged(auth.value, async (usr) => {
|
|
||||||
if (usr) {
|
|
||||||
user.value = usr
|
|
||||||
|
|
||||||
let docRef = doc(db, "users", user.value.uid);
|
|
||||||
let docSnap = await getDoc(docRef);
|
|
||||||
|
|
||||||
if (docSnap.exists()) {
|
|
||||||
const data = docSnap.data()
|
|
||||||
userData.value = data
|
|
||||||
getPersons(userData.value.relatiecodes)
|
|
||||||
} else {
|
|
||||||
setTimeout(() => window.location.reload(true), 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userData.value.sendNews && route.path === '/news/newmessage') navigateTo('/')
|
|
||||||
if (!userData.value.admin && route.path.startsWith('/settings/admin')) navigateTo('/')
|
|
||||||
|
|
||||||
isAuthenticated.value = true
|
|
||||||
|
|
||||||
logDeviceInfo()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
isAuthenticated.value = false
|
|
||||||
user.value = null
|
|
||||||
userData.value = null
|
|
||||||
userPersons.value = []
|
|
||||||
}
|
|
||||||
|
|
||||||
userLoaded.value = true
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const getPersons = async (persons) => {
|
|
||||||
userPersons.value = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < persons.length; i++) {
|
|
||||||
const docRef = doc(db, "ledenlijst", persons[i]);
|
|
||||||
const docSnap = await getDoc(docRef);
|
|
||||||
|
|
||||||
if (docSnap.exists()) {
|
|
||||||
userPersons.value.push(docSnap.data())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setupNotifications = () => {
|
|
||||||
console.log('Initializing HomePage');
|
|
||||||
|
|
||||||
// Request permission to use push notifications
|
|
||||||
// iOS will prompt user and return if they granted permission or not
|
|
||||||
// Android will just grant without prompting
|
|
||||||
PushNotifications.requestPermissions().then(result => {
|
|
||||||
if (result.receive === 'granted') {
|
|
||||||
// Register with Apple / Google to receive push via APNS/FCM
|
|
||||||
PushNotifications.register()
|
|
||||||
} else {
|
|
||||||
toast.error('Error tijdens het registrenen van push notificaties')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// On success, we should be able to receive notifications
|
|
||||||
PushNotifications.addListener('registration',
|
|
||||||
async (token) => {
|
|
||||||
// alert('Push registration success, token: ' + token.value);
|
|
||||||
registrationToken.value = token
|
|
||||||
|
|
||||||
const { error } = await useFetch('/api/subscribetotopic', {
|
|
||||||
method: 'post',
|
|
||||||
body: { topic: 'all', registrationToken: token.value }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (error.value) {
|
|
||||||
console.log(error.value)
|
|
||||||
return toast.error('Error tijdens het krijgen van relateicodes')
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Subscribed to topic!')
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Some issue with our setup and push will not work
|
|
||||||
PushNotifications.addListener('registrationError',
|
|
||||||
(error) => {
|
|
||||||
toast.error('Error tijdens het registreren van push notificaties')
|
|
||||||
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Show us the notification payload if the app is open on our device
|
|
||||||
PushNotifications.addListener('pushNotificationReceived',
|
|
||||||
(notification) => {
|
|
||||||
|
|
||||||
toast.info(`${notification.title}`, {
|
|
||||||
onClick: () => navigateTo('/news')
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Method called when tapping on a notification
|
|
||||||
PushNotifications.addListener('pushNotificationActionPerformed',
|
|
||||||
(notification) => {
|
|
||||||
navigateTo('/news')
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const registerFCM = () => {
|
|
||||||
getToken(messaging.value, { vapidKey: 'BI7l3nyGV6wJcFh7wrwmQ42W7RSXl46bmhXZJmDd4P-0K_JFP0ClTqjO-rr5H5DXBbmVR4kXwxFpUlo_d6cUy4Q' }).then(async (currentToken) => {
|
|
||||||
if (currentToken) {
|
|
||||||
console.log(currentToken)
|
|
||||||
|
|
||||||
const { error} = await useFetch('/api/subscribetotopic', {
|
|
||||||
method: 'post',
|
|
||||||
body: { topic: 'all', registrationToken: currentToken }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (error.value) {
|
|
||||||
console.log(error.value)
|
|
||||||
return toast.error('Error tijdens het registreren van push notifications')
|
|
||||||
}
|
|
||||||
|
|
||||||
registrationToken.value = currentToken
|
|
||||||
console.log('Subscribed to topic!')
|
|
||||||
} else {
|
|
||||||
// Show permission request UI
|
|
||||||
console.log('No registration token available. Request permission to generate one.');
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.log('An error occurred while retrieving token. ', err);
|
|
||||||
// ...
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const registerSW = () => {
|
|
||||||
messaging.value = getMessaging()
|
|
||||||
navigator.serviceWorker.register('/sw.js').catch(e => alert(e));
|
|
||||||
|
|
||||||
onMessage(messaging.value, (payload) => {
|
|
||||||
console.log('Message received. ', payload);
|
|
||||||
|
|
||||||
toast.info(`${payload.notification.title}`, {
|
|
||||||
onClick: () => navigateTo('/news')
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const logDeviceInfo = async () => {
|
|
||||||
const info = await Device.getInfo();
|
|
||||||
|
|
||||||
console.log(info);
|
|
||||||
|
|
||||||
if (info.platform !== 'web') setupNotifications()
|
|
||||||
else registerFCM()
|
|
||||||
};
|
|
||||||
|
|
||||||
const ledenlijst = ref([])
|
|
||||||
|
|
||||||
provide('firebase', { db, ledenlijst, isAuthenticated, user, userData, userPersons, auth, users, userAllPersons, getPersons, calEvents, news, registrationToken, contestTimes, competitors, registrationToken })
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -43,17 +43,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, sendPasswordResetEmail } from "firebase/auth";
|
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, sendPasswordResetEmail } from "firebase/auth";
|
||||||
import { doc, setDoc } from "firebase/firestore";
|
import { doc, setDoc, getFirestore } from "firebase/firestore";
|
||||||
|
|
||||||
|
|
||||||
const { auth, db, userAllPersons } = inject('firebase')
|
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const showPassword = ref(false)
|
const showPassword = ref(false)
|
||||||
const creatingAccount = ref(false)
|
const creatingAccount = ref(false)
|
||||||
const disableButtons = ref(false)
|
const disableButtons = ref(false)
|
||||||
|
|
||||||
|
const db = getFirestore()
|
||||||
|
|
||||||
const form = ref({
|
const form = ref({
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
@ -63,8 +63,7 @@ const form = ref({
|
|||||||
|
|
||||||
const submitLoginForm = () => {
|
const submitLoginForm = () => {
|
||||||
disableButtons.value = true
|
disableButtons.value = true
|
||||||
|
signInWithEmailAndPassword(userStore.auth, form.value.email, form.value.password)
|
||||||
signInWithEmailAndPassword(auth.value, form.value.email, form.value.password)
|
|
||||||
.then(() => disableButtons.value = false)
|
.then(() => disableButtons.value = false)
|
||||||
.catch(async (error) => {
|
.catch(async (error) => {
|
||||||
|
|
||||||
@ -101,9 +100,9 @@ const submitCreateForm = () => {
|
|||||||
|
|
||||||
disableButtons.value = true
|
disableButtons.value = true
|
||||||
|
|
||||||
createUserWithEmailAndPassword(auth.value, form.value.email, form.value.newPassword)
|
createUserWithEmailAndPassword(userStore.auth, form.value.email, form.value.newPassword)
|
||||||
.then(async (userCredential) => {
|
.then(async (userCredential) => {
|
||||||
const idToken = await auth.value.currentUser.getIdToken(true)
|
const idToken = await userStore.auth.currentUser.getIdToken(true)
|
||||||
const { error, data } = await useFetch('/api/getrelatiecodes', {
|
const { error, data } = await useFetch('/api/getrelatiecodes', {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
body: { email: form.value.email, token: idToken }
|
body: { email: form.value.email, token: idToken }
|
||||||
@ -132,7 +131,7 @@ const submitCreateForm = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
userAllPersons.value = data.value.persons
|
userStore.userAllPersons = data.value.persons
|
||||||
|
|
||||||
if (data.value.relatiecodes.length > 1) {
|
if (data.value.relatiecodes.length > 1) {
|
||||||
return navigateTo('/settings/config/managerelatiecodes')
|
return navigateTo('/settings/config/managerelatiecodes')
|
||||||
@ -150,7 +149,7 @@ const submitCreateForm = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const forgotPassword = () => {
|
const forgotPassword = () => {
|
||||||
sendPasswordResetEmail(auth.value, form.value.email)
|
sendPasswordResetEmail(userStore.auth, form.value.email)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.info('Wachtwoord vergeten email verstuurd!')
|
toast.info('Wachtwoord vergeten email verstuurd!')
|
||||||
})
|
})
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<Icon size="1.8em" name="ion:settings-sharp" />
|
<Icon size="1.8em" name="ion:settings-sharp" />
|
||||||
<span>Settings</span>
|
<span>Settings</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink v-if="userPersons[0] && userPersons.filter(a => a.wedstrijdteam).length > 0" to="/wedstrijd" class="flex flex-col items-center hover:cursor-pointer drop-shadow" :class="route.path.startsWith('/wedstrijd') ? 'text-primary' : ''">
|
<NuxtLink v-if="userStore.userPersons[0] && userStore.userPersons.filter(a => a.wedstrijdteam).length > 0" to="/wedstrijd" class="flex flex-col items-center hover:cursor-pointer drop-shadow" :class="route.path.startsWith('/wedstrijd') ? 'text-primary' : ''">
|
||||||
<Icon size="1.8em" name="ion:podium-outline" />
|
<Icon size="1.8em" name="ion:podium-outline" />
|
||||||
<span>Wedstrijd</span>
|
<span>Wedstrijd</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
import { Device } from '@capacitor/device';
|
import { Device } from '@capacitor/device';
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { userData, userPersons } = inject('firebase')
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const platform = ref(null)
|
const platform = ref(null)
|
||||||
|
|
||||||
|
123
frontend/composables/notifications.js
Normal file
123
frontend/composables/notifications.js
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { getMessaging, getToken, onMessage } from 'firebase/messaging'
|
||||||
|
import { PushNotifications } from '@capacitor/push-notifications';
|
||||||
|
import { Device } from '@capacitor/device';
|
||||||
|
|
||||||
|
|
||||||
|
export const setupIosNotifications = () => {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
// Request permission to use push notifications
|
||||||
|
// iOS will prompt user and return if they granted permission or not
|
||||||
|
// Android will just grant without prompting
|
||||||
|
PushNotifications.requestPermissions().then(result => {
|
||||||
|
if (result.receive === 'granted') {
|
||||||
|
// Register with Apple / Google to receive push via APNS/FCM
|
||||||
|
PushNotifications.register()
|
||||||
|
} else {
|
||||||
|
toast.error('Error tijdens het registrenen van push notificaties')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// On success, we should be able to receive notifications
|
||||||
|
PushNotifications.addListener('registration',
|
||||||
|
async (token) => {
|
||||||
|
// alert('Push registration success, token: ' + token.value);
|
||||||
|
userStore.registrationToken = token
|
||||||
|
|
||||||
|
const { error } = await useFetch('/api/subscribetotopic', {
|
||||||
|
method: 'post',
|
||||||
|
body: { topic: 'all', registrationToken: token.value }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.log(error.value)
|
||||||
|
return toast.error('Error tijdens het krijgen van relateicodes')
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Subscribed to topic!')
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Some issue with our setup and push will not work
|
||||||
|
PushNotifications.addListener('registrationError',
|
||||||
|
(error) => {
|
||||||
|
toast.error('Error tijdens het registreren van push notificaties')
|
||||||
|
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show us the notification payload if the app is open on our device
|
||||||
|
PushNotifications.addListener('pushNotificationReceived',
|
||||||
|
(notification) => {
|
||||||
|
|
||||||
|
toast.info(`${notification.title}`, {
|
||||||
|
onClick: () => navigateTo('/news')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Method called when tapping on a notification
|
||||||
|
PushNotifications.addListener('pushNotificationActionPerformed',
|
||||||
|
(notification) => {
|
||||||
|
navigateTo('/news')
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setupWebNotifications = () => {
|
||||||
|
const messaging = getMessaging()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
getToken(messaging, { vapidKey: 'BI7l3nyGV6wJcFh7wrwmQ42W7RSXl46bmhXZJmDd4P-0K_JFP0ClTqjO-rr5H5DXBbmVR4kXwxFpUlo_d6cUy4Q' }).then(async (currentToken) => {
|
||||||
|
if (currentToken) {
|
||||||
|
console.log(currentToken)
|
||||||
|
|
||||||
|
const { error} = await useFetch('/api/subscribetotopic', {
|
||||||
|
method: 'post',
|
||||||
|
body: { topic: 'all', registrationToken: currentToken }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.log(error.value)
|
||||||
|
return toast.error('Error tijdens het registreren van push notifications')
|
||||||
|
}
|
||||||
|
|
||||||
|
userStore.registrationToken = currentToken
|
||||||
|
console.log('Subscribed to topic!')
|
||||||
|
} else {
|
||||||
|
// Show permission request UI
|
||||||
|
console.log('No registration token available. Request permission to generate one.');
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log('An error occurred while retrieving token. ', err);
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setupNotifications = async () => {
|
||||||
|
const info = await Device.getInfo();
|
||||||
|
|
||||||
|
console.log(info);
|
||||||
|
|
||||||
|
if (info.platform !== 'web') setupIosNotifications()
|
||||||
|
else setupWebNotifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const registerServiceWorker = () => {
|
||||||
|
const messaging = getMessaging()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
navigator.serviceWorker.register('/sw.js').catch(e => alert(e));
|
||||||
|
|
||||||
|
onMessage(messaging, (payload) => {
|
||||||
|
console.log('Message received. ', payload);
|
||||||
|
|
||||||
|
toast.info(`${payload.notification.title}`, {
|
||||||
|
onClick: () => navigateTo('/news')
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
6
frontend/composables/useToast.js
Normal file
6
frontend/composables/useToast.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { useToast } from 'vue-toastification'
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const toast = useToast()
|
||||||
|
return toast
|
||||||
|
}
|
@ -1,34 +1,38 @@
|
|||||||
// https://v3.nuxtjs.org/api/configuration/nuxt.config
|
// https://v3.nuxtjs.org/api/configuration/nuxt.config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
ssr: false,
|
ssr: false,
|
||||||
modules: [
|
imports: {
|
||||||
'@nuxtjs/tailwindcss',
|
dirs: ['stores']
|
||||||
'nuxt-icon',
|
},
|
||||||
'@vueuse/nuxt',
|
modules: [
|
||||||
'@nuxtjs/robots',
|
'@nuxtjs/tailwindcss',
|
||||||
'@nuxtjs/plausible',
|
'nuxt-icon',
|
||||||
],
|
'@vueuse/nuxt',
|
||||||
plausible: {
|
'@nuxtjs/robots',
|
||||||
domain: 'wrbapp.xeovalyte.com',
|
'@nuxtjs/plausible',
|
||||||
apiHost: 'https://plausible.xeovalyte.dev',
|
[ '@pinia/nuxt', { autoImports: [ 'defineStore' ]} ],
|
||||||
},
|
],
|
||||||
build: {
|
plausible: {
|
||||||
transpile: ['vue-toastification'],
|
domain: 'wrbapp.xeovalyte.com',
|
||||||
},
|
apiHost: 'https://plausible.xeovalyte.dev',
|
||||||
app: {
|
},
|
||||||
head: {
|
build: {
|
||||||
title: 'WRB App',
|
transpile: ['vue-toastification'],
|
||||||
charset: 'utf-8',
|
},
|
||||||
viewport: 'width=device-width initial-scale=1 viewport-fit=cover',
|
app: {
|
||||||
meta: [
|
head: {
|
||||||
{ name: 'theme-color', content: '#eb6330' },
|
title: 'WRB App',
|
||||||
{ name: 'description', content: 'De officiele app voor de Waddinxveense Reddingsbrigade'}
|
charset: 'utf-8',
|
||||||
],
|
viewport: 'width=device-width initial-scale=1 viewport-fit=cover',
|
||||||
link: [
|
meta: [
|
||||||
{ rel: 'manifest', href: '/manifest.json' },
|
{ name: 'theme-color', content: '#eb6330' },
|
||||||
{ rel: 'icon', href: '/favicon.ico', type: 'image/x-icon' }
|
{ name: 'description', content: 'De officiele app voor de Waddinxveense Reddingsbrigade'}
|
||||||
]
|
],
|
||||||
}
|
link: [
|
||||||
|
{ rel: 'manifest', href: '/manifest.json' },
|
||||||
|
{ rel: 'icon', href: '/favicon.ico', type: 'image/x-icon' }
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
privateKeyId: '',
|
privateKeyId: '',
|
||||||
|
4098
frontend/package-lock.json
generated
4098
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"overrides": {
|
||||||
|
"vue": "latest"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt build",
|
"build": "nuxt build",
|
||||||
"dev": "nuxt dev",
|
"dev": "nuxt dev",
|
||||||
@ -8,27 +11,29 @@
|
|||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@capacitor/cli": "^4.6.3",
|
"@capacitor/cli": "^4.7.1",
|
||||||
"@nuxtjs/plausible": "^0.2.0",
|
"@nuxtjs/plausible": "^0.2.0",
|
||||||
"@nuxtjs/tailwindcss": "^6.3.1",
|
"@nuxtjs/tailwindcss": "^6.6.0",
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@vueuse/core": "^9.12.0",
|
"@vueuse/core": "^9.13.0",
|
||||||
"@vueuse/nuxt": "^9.12.0",
|
"@vueuse/nuxt": "^9.13.0",
|
||||||
"nuxt": "^3.2.0",
|
"nuxt": "^3.3.1",
|
||||||
"nuxt-icon": "^0.2.11"
|
"nuxt-icon": "^0.3.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor/core": "^4.6.3",
|
"@capacitor/core": "^4.7.1",
|
||||||
"@capacitor/device": "^4.1.0",
|
"@capacitor/device": "^4.1.0",
|
||||||
"@capacitor/ios": "^4.6.3",
|
"@capacitor/ios": "^4.7.1",
|
||||||
"@capacitor/push-notifications": "^4.1.2",
|
"@capacitor/push-notifications": "^4.1.2",
|
||||||
"@formkit/nuxt": "^1.0.0-beta.11-c95e605",
|
"@formkit/nuxt": "^1.0.0-beta.11-c95e605",
|
||||||
"@nuxtjs/robots": "^3.0.0",
|
"@nuxtjs/robots": "^3.0.0",
|
||||||
"@vueuse/components": "^9.12.0",
|
"@pinia/nuxt": "^0.4.7",
|
||||||
"@vueuse/firebase": "^9.12.0",
|
"@vueuse/components": "^9.13.0",
|
||||||
"@vueuse/shared": "^9.12.0",
|
"@vueuse/firebase": "^9.13.0",
|
||||||
"firebase": "^9.17.1",
|
"@vueuse/shared": "^9.13.0",
|
||||||
|
"firebase": "^9.18.0",
|
||||||
"firebase-admin": "^11.5.0",
|
"firebase-admin": "^11.5.0",
|
||||||
|
"pinia": "^2.0.33",
|
||||||
"vue-toastification": "^2.0.0-rc.5"
|
"vue-toastification": "^2.0.0-rc.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,96 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md">
|
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md">
|
||||||
<div v-if="calEvents[0]" class="flex flex-col gap-3">
|
<div v-if="calendarStore.events[0]" class="flex flex-col gap-3">
|
||||||
<div v-for="(event, index) in calEvents" :key="index">
|
<div v-for="(event, index) in calendarStore.events" :key="index">
|
||||||
<div class="item container flex flex-col">
|
<div class="item container flex flex-col">
|
||||||
<h2 class="">{{ longEventDate(event.date) }}</h2>
|
<h2 class="">{{ longEventDate(event.date) }}</h2>
|
||||||
<p class="whitespace-pre overflow-x-auto font-bold text-xl">{{ event.description}}</p>
|
<p class="whitespace-pre overflow-x-auto font-bold text-xl">{{ event.description}}</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full flex flex-col justify-center items-center" v-else>
|
|
||||||
<Icon size="2em" name="ion:load-c" class="animate-spin" />
|
|
||||||
<h2 class="mt-2 font-bold">Loading...</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full flex flex-col justify-center items-center" v-else>
|
||||||
|
<Icon size="2em" name="ion:load-c" class="animate-spin" />
|
||||||
|
<h2 class="mt-2 font-bold">Loading...</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
title: 'Agenda'
|
title: 'Agenda'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { userPersons, calEvents } = inject('firebase')
|
const userStore = useUserStore()
|
||||||
|
const calendarStore = useCalendarStore()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!calEvents[0]) getCalendarEvents([...new Set(userPersons.value.map(a => a.groups.join()).join().split(','))])
|
getEvents()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getEvents = () => {
|
||||||
|
|
||||||
|
if (userStore.userPersons[0]) {
|
||||||
|
const groups = [...new Set(userStore.userPersons.map(a => a.groups.join()).join().split(','))]
|
||||||
|
|
||||||
|
calendarStore.getEvents(groups)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => { getEvents() }, 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const longEventDate = (eventDate) => {
|
const longEventDate = (eventDate) => {
|
||||||
const date = new Date(eventDate)
|
const date = new Date(eventDate)
|
||||||
|
|
||||||
return date.toLocaleString('nl-NL', {
|
return date.toLocaleString('nl-NL', {
|
||||||
weekday: 'short',
|
weekday: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric'
|
minute: 'numeric'
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|
||||||
const getCalendarEvents = async (group) => {
|
|
||||||
|
|
||||||
if (!group[0]) return setTimeout(() => { getCalendarEvents([...new Set(userPersons.value.map(a => a.groups.join()).join().split(','))])}, 50)
|
|
||||||
|
|
||||||
const events = []
|
|
||||||
let fridayEvents = []
|
|
||||||
let saturdayEvents = []
|
|
||||||
let matchEvents = []
|
|
||||||
|
|
||||||
if (group.includes('Zaterdag')){
|
|
||||||
let data = await fetch('https://www.googleapis.com/calendar/v3/calendars/c_astg0d0auheip5o4269v1qvv3g@group.calendar.google.com/events?key=AIzaSyBLmxWNEnbkiW_0c2UrsHMbxXv73dX-KYw')
|
|
||||||
if (!data.ok){
|
|
||||||
throw Error('No data available')
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonEvents = await data.json()
|
|
||||||
saturdayEvents = jsonEvents.items
|
|
||||||
} if (group.includes('Vrijdag')){
|
|
||||||
let data = await fetch('https://www.googleapis.com/calendar/v3/calendars/c_u3895n01jt8qnusm7f6pmqjb0k%40group.calendar.google.com/events?key=AIzaSyBLmxWNEnbkiW_0c2UrsHMbxXv73dX-KYw')
|
|
||||||
if (!data.ok){
|
|
||||||
throw Error('No data available')
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonEvents = await data.json()
|
|
||||||
fridayEvents = jsonEvents.items
|
|
||||||
} if (group.includes('Wedstrijd')){
|
|
||||||
|
|
||||||
let data = await fetch('https://www.googleapis.com/calendar/v3/calendars/c_c2296iboq07n24galuobeesovs@group.calendar.google.com/events?key=AIzaSyBLmxWNEnbkiW_0c2UrsHMbxXv73dX-KYw')
|
|
||||||
if (!data.ok){
|
|
||||||
throw Error('No data available')
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonEvents = await data.json()
|
|
||||||
matchEvents = jsonEvents.items
|
|
||||||
}
|
|
||||||
|
|
||||||
const allEvents = [...fridayEvents, ...saturdayEvents, ...matchEvents]
|
|
||||||
|
|
||||||
const now = new Date()
|
|
||||||
now.setHours(0, 0, 0, 0)
|
|
||||||
|
|
||||||
const sortedEvents = allEvents.sort((a, b) => new Date(a.start.dateTime) - new Date(b.start.dateTime))
|
|
||||||
const filteredEvents = sortedEvents.filter((event) => new Date(event.start.dateTime) > now )
|
|
||||||
|
|
||||||
filteredEvents.forEach(element => {
|
|
||||||
events.push({
|
|
||||||
description: element.description,
|
|
||||||
date: new Date(element.start.dateTime)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
calEvents.value = events
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col justify-center items-center px-2 overflow-hidden">
|
<div class="flex flex-col justify-center items-center px-2 overflow-hidden">
|
||||||
<h1 class="font-bold text-3xl text-center mt-6 mb-3">Reddingsbrigade Waddinxveen</h1>
|
<h1 class="font-bold text-3xl text-center mt-6 mb-3">Reddingsbrigade Waddinxveen</h1>
|
||||||
<h2 class="text-xl text-center mb-12">{{ userPersons.map(a => a.fullName).join(', ')}}</h2>
|
<h2 class="text-xl text-center mb-12">{{ userStore.userPersons.map(a => a.fullName).join(', ')}}</h2>
|
||||||
<div class="container w-full max-w-md">
|
<div class="container w-full max-w-md">
|
||||||
<NuxtLink to="/news" class="rounded-t item-hover py-2 flex items-center">
|
<NuxtLink to="/news" class="rounded-t item-hover py-2 flex items-center">
|
||||||
<span>Nieuws</span>
|
<span>Nieuws</span>
|
||||||
@ -24,8 +24,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
title: 'Home',
|
title: 'Home',
|
||||||
})
|
})
|
||||||
|
const userStore = useUserStore()
|
||||||
const { userPersons } = inject('firebase')
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,92 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md">
|
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md">
|
||||||
<div v-if="loadedNews && news" class="flex flex-col gap-3">
|
<div v-if="newsStore.loaded && newsStore.news" class="flex flex-col gap-3">
|
||||||
<NuxtLink to="/news/newmessage" v-if="userData.sendNews" class="item-hover border-dashed border-2 container text-center font-bold text-xl border-neutral-500 mb-3">
|
<NuxtLink to="/news/newmessage" v-if="userStore.userData.sendNews" class="item-hover border-dashed border-2 container text-center font-bold text-xl border-neutral-500 mb-3">
|
||||||
Nieuw Bericht
|
Nieuw Bericht
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<div v-if="news[0]" v-for="(item, index) in news" :key="index">
|
<div v-if="newsStore.news[0]" v-for="(item, index) in newsStore.news" :key="index">
|
||||||
<div class="item container flex flex-col relative">
|
<div class="item container flex flex-col relative">
|
||||||
<h3 class="text-sm">{{ longEventDate(item.date.toDate()) }}</h3>
|
<h3 class="text-sm">{{ longEventDate(item.date.toDate()) }}</h3>
|
||||||
<h2 class="text-2xl font-bold">{{ item.title }}</h2>
|
<h2 class="text-2xl font-bold">{{ item.title }}</h2>
|
||||||
<p>{{ item.description }}</p>
|
<p>{{ item.description }}</p>
|
||||||
<Icon v-if="userData.sendNews" @click="deleteItem(item, index)" size="1.5em" name="ion:trash-sharp" class="absolute top-3 right-3 hover:cursor-pointer text-red-500" />
|
<Icon v-if="userStore.userData.sendNews" @click="newsStore.deleteNews(item, index)" size="1.5em" name="ion:trash-sharp" class="absolute top-3 right-3 hover:cursor-pointer text-red-500" />
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h2 v-else class="font-bold text-center text-xl mt-3">
|
|
||||||
Er is geen nieuws
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div class="w-full flex flex-col justify-center items-center" v-else>
|
|
||||||
<Icon size="2em" name="ion:load-c" class="animate-spin" />
|
|
||||||
<h2 class="mt-2 font-bold">Loading...</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<h2 v-else class="font-bold text-center text-xl mt-3">
|
||||||
|
Er is geen nieuws
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full flex flex-col justify-center items-center" v-else>
|
||||||
|
<Icon size="2em" name="ion:load-c" class="animate-spin" />
|
||||||
|
<h2 class="mt-2 font-bold">Loading...</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getDocs, collection, deleteDoc, doc } from 'firebase/firestore'
|
|
||||||
import { useToast } from 'vue-toastification'
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
title: 'Nieuws'
|
title: 'Nieuws'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { news, userData, db } = inject('firebase')
|
const userStore = useUserStore()
|
||||||
|
const newsStore = useNewsStore()
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
const loadedNews = ref(false)
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!news.value) getNews()
|
newsStore.getNews()
|
||||||
else loadedNews.value = true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const longEventDate = (eventDate) => {
|
const longEventDate = (eventDate) => {
|
||||||
const date = new Date(eventDate)
|
const date = new Date(eventDate)
|
||||||
|
|
||||||
return date.toLocaleString('nl-NL', {
|
return date.toLocaleString('nl-NL', {
|
||||||
weekday: 'short',
|
weekday: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric'
|
minute: 'numeric'
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|
||||||
const getNews = async () => {
|
|
||||||
news.value = []
|
|
||||||
try {
|
|
||||||
const querySnapshot = await getDocs(collection(db, "news"));
|
|
||||||
querySnapshot.forEach((doc) => {
|
|
||||||
let data = doc.data()
|
|
||||||
data.id = doc.id
|
|
||||||
news.value.push(data)
|
|
||||||
});
|
|
||||||
|
|
||||||
news.value.sort((a, b) => b.date.seconds - a.date.seconds)
|
|
||||||
|
|
||||||
loadedNews.value = true
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteItem = async (item, index) => {
|
|
||||||
if (!item.id) return toast.error('Refresh eerst voordat je dit bericht kan verwijderen')
|
|
||||||
|
|
||||||
try {
|
|
||||||
news.value.splice(index, 1)
|
|
||||||
|
|
||||||
await deleteDoc(doc(db, "news", item.id));
|
|
||||||
toast.success('Bericht verwijderd')
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
|
|
||||||
toast.error('Error tijdens bericht verwijderen')
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
@ -22,66 +22,35 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { addDoc, collection, serverTimestamp, Timestamp } from 'firebase/firestore'
|
|
||||||
import { useToast } from 'vue-toastification'
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
title: 'Nieuw Bericht',
|
title: 'Nieuw Bericht',
|
||||||
key: 'back'
|
key: 'back'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { news, db, auth } = inject('firebase')
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const newsStore = useNewsStore()
|
||||||
|
|
||||||
const disableButtons = ref(false)
|
const disableButtons = ref(false)
|
||||||
|
|
||||||
|
const sendNews = async () => {
|
||||||
|
try {
|
||||||
|
disableButtons.value = true
|
||||||
|
|
||||||
|
await newsStore.send(form.value)
|
||||||
|
|
||||||
|
disableButtons.value = false
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
disableButtons.value = false
|
||||||
|
|
||||||
|
toast.error('Error tijdens versturen bericht')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const form = ref({
|
const form = ref({
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
topic: ''
|
topic: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const sendNews = async () => {
|
|
||||||
disableButtons.value = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const idToken = await auth.value.currentUser.getIdToken(true)
|
|
||||||
console.log(idToken)
|
|
||||||
|
|
||||||
const { error } = await useFetch('/api/sendmessage', {
|
|
||||||
method: 'post',
|
|
||||||
body: { title: form.value.title, body: form.value.description, token: idToken, topic: form.value.topic }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (error.value) {
|
|
||||||
console.log(error.value)
|
|
||||||
return toast.error('Error tijdens het versturen van het bericht')
|
|
||||||
}
|
|
||||||
|
|
||||||
await addDoc(collection(db, "news"), {
|
|
||||||
title: form.value.title,
|
|
||||||
description: form.value.description,
|
|
||||||
date: serverTimestamp()
|
|
||||||
});
|
|
||||||
|
|
||||||
if (news.value) {
|
|
||||||
news.value.unshift({
|
|
||||||
title: form.value.title,
|
|
||||||
description: form.value.description,
|
|
||||||
date: Timestamp.now()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
toast.success('Bericht is verstuurd')
|
|
||||||
|
|
||||||
navigateTo('/news')
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
|
|
||||||
toast.error('Error tijdens het berict sturen')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
disableButtons.value = false
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { doc, getDocs, collection, writeBatch, updateDoc, setDoc } from "firebase/firestore";
|
import { doc, getDocs, collection, writeBatch, updateDoc, setDoc, getFirestore } from "firebase/firestore";
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@ -50,11 +50,11 @@ definePageMeta({
|
|||||||
key: 'back'
|
key: 'back'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { db, ledenlijst, users } = inject('firebase')
|
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
|
const usersStore = useUsersStore()
|
||||||
const modelData = ref(null)
|
const modelData = ref(null)
|
||||||
|
const db = getFirestore()
|
||||||
|
|
||||||
const file = ref(null)
|
const file = ref(null)
|
||||||
const disableButtons = ref(false)
|
const disableButtons = ref(false)
|
||||||
@ -63,18 +63,7 @@ const newLedenlijst = ref([])
|
|||||||
const showModel = ref(false)
|
const showModel = ref(false)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (!ledenlijst.value.length) {
|
usersStore.getLedenlijst()
|
||||||
try {
|
|
||||||
const querySnapshot = await getDocs(collection(db, "ledenlijst"));
|
|
||||||
querySnapshot.forEach((doc) => {
|
|
||||||
ledenlijst.value.push(doc.data())
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
ledenlijst.value.sort((a, b) => a.fullName.localeCompare(b.fullName))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleModel = (lid) => {
|
const handleModel = (lid) => {
|
||||||
@ -88,7 +77,7 @@ const handleModel = (lid) => {
|
|||||||
|
|
||||||
const submitModelForm = async () => {
|
const submitModelForm = async () => {
|
||||||
disableButtons.value = true
|
disableButtons.value = true
|
||||||
ledenlijst.value.filter(a => a.relatiecode === modelData.value.relatiecode)[0].wedstrijdteam = modelData.value.wedstrijdteam
|
usersStore.ledenlijst.filter(a => a.relatiecode === modelData.value.relatiecode)[0].wedstrijdteam = modelData.value.wedstrijdteam
|
||||||
|
|
||||||
if (modelData.value.wedstrijdteam === modelData.value.oldWedstrijdteam) {
|
if (modelData.value.wedstrijdteam === modelData.value.oldWedstrijdteam) {
|
||||||
disableButtons.value = false
|
disableButtons.value = false
|
||||||
@ -123,7 +112,7 @@ const submitModelForm = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const filteredLedenlijst = computed(() => {
|
const filteredLedenlijst = computed(() => {
|
||||||
return ledenlijst.value.filter(lid => lid.fullName.toLowerCase().includes(searchTerm.value.toLowerCase()))
|
return usersStore.ledenlijst.filter(lid => lid.fullName.toLowerCase().includes(searchTerm.value.toLowerCase()))
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleFileChanged = (event) => {
|
const handleFileChanged = (event) => {
|
||||||
@ -224,7 +213,7 @@ const uploadLedenlijst = async () => {
|
|||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
ledenlijst.value = newLedenlijst.value
|
usersStore.ledenlijst = newLedenlijst.value
|
||||||
|
|
||||||
updateUsers()
|
updateUsers()
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getDocs, collection, doc, updateDoc } from 'firebase/firestore'
|
import { getDocs, collection, doc, updateDoc, getFirestore } from 'firebase/firestore'
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@ -52,8 +52,9 @@ definePageMeta({
|
|||||||
key: 'back'
|
key: 'back'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { users, db } = inject('firebase')
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const db = getFirestore()
|
||||||
|
const usersStore = useUsersStore()
|
||||||
|
|
||||||
const searchTerm = ref('')
|
const searchTerm = ref('')
|
||||||
const disableButtons = ref(false)
|
const disableButtons = ref(false)
|
||||||
@ -62,16 +63,7 @@ const showModel = ref(false)
|
|||||||
const modelData = ref({})
|
const modelData = ref({})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (!users.value.length) {
|
usersStore.getUsers()
|
||||||
try {
|
|
||||||
const querySnapshot = await getDocs(collection(db, "users"));
|
|
||||||
querySnapshot.forEach((doc) => {
|
|
||||||
users.value.push(doc.data())
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleModel = (user) => {
|
const handleModel = (user) => {
|
||||||
@ -84,7 +76,7 @@ const handleModel = (user) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const filteredUsers = computed(() => {
|
const filteredUsers = computed(() => {
|
||||||
return users.value.filter(user => user.email.toLowerCase().includes(searchTerm.value.toLowerCase()))
|
return usersStore.users.filter(user => user.email.toLowerCase().includes(searchTerm.value.toLowerCase()))
|
||||||
})
|
})
|
||||||
|
|
||||||
const submitModelForm = async () => {
|
const submitModelForm = async () => {
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md">
|
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md">
|
||||||
<form @submit.prevent="savePassword" class="flex flex-col">
|
<form @submit.prevent="savePassword" class="flex flex-col">
|
||||||
<label class="font-bold">Oud Wachtwoord</label>
|
<label class="font-bold">Oud Wachtwoord</label>
|
||||||
<input v-model="form.oldPassword" required="true" class="input mb-5" :type="showPassword ? 'text' : 'password'">
|
<input v-model="form.oldPassword" required="true" class="input mb-5" :type="showPassword ? 'text' : 'password'">
|
||||||
|
|
||||||
<label class="font-bold">Nieuw Wachtwoord</label>
|
<label class="font-bold">Nieuw Wachtwoord</label>
|
||||||
<input v-model="form.newPassword" required="true" class="input mb-5" :type="showPassword ? 'text' : 'password'">
|
<input v-model="form.newPassword" required="true" class="input mb-5" :type="showPassword ? 'text' : 'password'">
|
||||||
|
|
||||||
<label class="font-bold">Herhaal Nieuw Wachtwoord</label>
|
<label class="font-bold">Herhaal Nieuw Wachtwoord</label>
|
||||||
<input v-model="form.confirmNewPassword" required="true" class="input" :type="showPassword ? 'text' : 'password'">
|
<input v-model="form.confirmNewPassword" required="true" class="input" :type="showPassword ? 'text' : 'password'">
|
||||||
|
|
||||||
<div class="mb-5 mt-1 flex items-center text-default">
|
<div class="mb-5 mt-1 flex items-center text-default">
|
||||||
<input v-model="showPassword" type="checkbox" class="mr-1 checkbox ">
|
<input v-model="showPassword" type="checkbox" class="mr-1 checkbox ">
|
||||||
<span>Toon Wachtwoord</span>
|
<span>Toon Wachtwoord</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex flex-wrap justify-between">
|
<div class="w-full flex flex-wrap justify-between">
|
||||||
<input :disabled="disableButtons" type="submit" value="Wijzig Wachtwoord" class="btn w-full sm:w-52 mb-1">
|
<input :disabled="disableButtons" type="submit" value="Wijzig Wachtwoord" class="btn w-full sm:w-52 mb-1">
|
||||||
<button @click="router.back()" class="hover:underline font-bold w-full sm:w-max sm:ml-auto">Annuleer</button>
|
<button @click="router.back()" class="hover:underline font-bold w-full sm:w-max sm:ml-auto">Annuleer</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -28,60 +28,58 @@ import { reauthenticateWithCredential, EmailAuthProvider, updatePassword } from
|
|||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
title: 'Wachtwoord Wijzigen',
|
title: 'Wachtwoord Wijzigen',
|
||||||
key: 'back'
|
key: 'back'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { user, auth } = inject('firebase')
|
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const form = ref({
|
const form = ref({
|
||||||
oldPassword: '',
|
oldPassword: '',
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
confirmNewPassword: '',
|
confirmNewPassword: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
form.value = {
|
form.value = {
|
||||||
oldPassword: '',
|
oldPassword: '',
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
confirmNewPassword: '',
|
confirmNewPassword: '',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const savePassword = () => {
|
const savePassword = () => {
|
||||||
if (form.value.newPassword !== form.value.confirmNewPassword) return alert ('Niewe wachtwoorden zijn niet hetzelfde')
|
if (form.value.newPassword !== form.value.confirmNewPassword) return alert ('Niewe wachtwoorden zijn niet hetzelfde')
|
||||||
|
|
||||||
disableButtons.value = true
|
disableButtons.value = true
|
||||||
|
|
||||||
const credential = EmailAuthProvider.credential(
|
const credential = EmailAuthProvider.credential(
|
||||||
user.value.email,
|
userStore.user.email,
|
||||||
form.value.oldPassword
|
form.value.oldPassword
|
||||||
)
|
)
|
||||||
|
|
||||||
reauthenticateWithCredential(auth.value.currentUser, credential).then(() => {
|
reauthenticateWithCredential(userStore.auth.currentUser, credential).then(() => {
|
||||||
updatePassword(auth.value.currentUser, form.value.newPassword).then(() => {
|
updatePassword(userStore.auth.currentUser, form.value.newPassword).then(() => {
|
||||||
toast.success('Wachtwoord is veranderd')
|
toast.success('Wachtwoord is veranderd')
|
||||||
|
|
||||||
navigateTo('/settings')
|
navigateTo('/settings')
|
||||||
disableButtons.value = false
|
disableButtons.value = false
|
||||||
}).catch((error) => {
|
|
||||||
toast.error('Error tijdens het wachtwoord veranderen')
|
|
||||||
console.log(error)
|
|
||||||
disableButtons.value = false
|
|
||||||
});
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
disableButtons.value = false
|
toast.error('Error tijdens het wachtwoord veranderen')
|
||||||
|
console.log(error)
|
||||||
if (error.code === 'auth/wrong-password') return toast.error('Oude wachtwoord is onjuist')
|
disableButtons.value = false
|
||||||
|
|
||||||
toast.error('Error tijdens het wachtwoord veranderen')
|
|
||||||
|
|
||||||
console.log(error)
|
|
||||||
});
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
disableButtons.value = false
|
||||||
|
|
||||||
|
if (error.code === 'auth/wrong-password') return toast.error('Oude wachtwoord is onjuist')
|
||||||
|
|
||||||
|
toast.error('Error tijdens het wachtwoord veranderen')
|
||||||
|
|
||||||
|
console.log(error)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const showPassword = ref(false)
|
const showPassword = ref(false)
|
||||||
|
@ -1,52 +1,43 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md">
|
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md">
|
||||||
<div v-if="userAllPersons.length !== 0 && userPersons.length !== 0" class="flex flex-col gap-3">
|
<div v-if="userStore.userAllPersons.length !== 0 && userStore.userPersons.length !== 0" class="flex flex-col gap-3">
|
||||||
<div v-for="person in userAllPersons" :key="person.relatiecode">
|
<div v-for="person in userStore.userAllPersons" :key="person.relatiecode">
|
||||||
<div @click="updateCheckbox(person)" class="item container flex flex-wrap" :class="person.relatiecode === userPersons[0].relatiecode ? 'bg-neutral-200 dark:bg-neutral-850 text-neutral-400 dark:text-neutral-500 hover:cursor-not-allowed' : 'hover:cursor-pointer'">
|
<div @click="updateCheckbox(person)" class="item container flex flex-wrap" :class="person.relatiecode === userStore.userPersons[0].relatiecode ? 'bg-neutral-200 dark:bg-neutral-850 text-neutral-400 dark:text-neutral-500 hover:cursor-not-allowed' : 'hover:cursor-pointer'">
|
||||||
<input v-model="person.checked" :disabled="person.relatiecode === userPersons[0].relatiecode" class="checkbox my-auto mr-3 disabled:bg-neutral-300 disabled:hover:text-neutral-300 dark:disabled:bg-neutral-600 dark:disabled:hover:text-neutral-600 disabled:hover:cursor-not-allowed" type="checkbox">
|
<input v-model="person.checked" :disabled="person.relatiecode === userStore.userPersons[0].relatiecode" class="checkbox my-auto mr-3 disabled:bg-neutral-300 disabled:hover:text-neutral-300 dark:disabled:bg-neutral-600 dark:disabled:hover:text-neutral-600 disabled:hover:cursor-not-allowed" type="checkbox">
|
||||||
<span><b>{{ person.fullName }}</b></span>
|
<span><b>{{ person.fullName }}</b></span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full flex flex-wrap">
|
|
||||||
<button :disabled="buttonsDisabled" @click="save" class="btn w-full sm:w-40 mb-1">Opslaan</button>
|
|
||||||
<span @click="router.back()" class="hover:underline font-bold w-full text-center sm:w-max sm:ml-auto hover:cursor-pointer">Annuleer</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full flex flex-col justify-center items-center" v-else>
|
|
||||||
<Icon size="2em" name="ion:load-c" class="animate-spin" />
|
|
||||||
<h2 class="mt-2 font-bold">Loading...</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex flex-wrap">
|
||||||
|
<button :disabled="buttonsDisabled" @click="save" class="btn w-full sm:w-40 mb-1">Opslaan</button>
|
||||||
|
<span @click="router.back()" class="hover:underline font-bold w-full text-center sm:w-max sm:ml-auto hover:cursor-pointer">Annuleer</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full flex flex-col justify-center items-center" v-else>
|
||||||
|
<Icon size="2em" name="ion:load-c" class="animate-spin" />
|
||||||
|
<h2 class="mt-2 font-bold">Loading...</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { updateDoc, doc } from 'firebase/firestore'
|
import { updateDoc, doc, getFirestore } from 'firebase/firestore'
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
title: 'Beheer Personen',
|
title: 'Beheer Personen',
|
||||||
key: 'back'
|
key: 'back'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { user, userAllPersons, userPersons, db, getPersons, auth } = inject('firebase')
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const db = getFirestore()
|
||||||
|
|
||||||
const buttonsDisabled = ref(false)
|
const buttonsDisabled = ref(false)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (userAllPersons.value.length === 0) {
|
userStore.getAllPersons()
|
||||||
getAllPersons()
|
|
||||||
} else {
|
|
||||||
userAllPersons.value.forEach(person => {
|
|
||||||
if (userPersons.value.map(a => a.relatiecode).includes(person.relatiecode)) {
|
|
||||||
person.checked = true
|
|
||||||
} else {
|
|
||||||
person.checked = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
@ -54,52 +45,25 @@ const save = async () => {
|
|||||||
|
|
||||||
const newRelatiecodes = []
|
const newRelatiecodes = []
|
||||||
|
|
||||||
userAllPersons.value.forEach(person => {
|
userStore.userAllPersons.forEach(person => {
|
||||||
if (person.checked) {
|
if (person.checked) {
|
||||||
newRelatiecodes.push(person.relatiecode)
|
newRelatiecodes.push(person.relatiecode)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
await updateDoc(doc(db, "users", user.value.uid), {
|
await updateDoc(doc(db, "users", userStore.user.uid), {
|
||||||
relatiecodes: newRelatiecodes
|
relatiecodes: newRelatiecodes
|
||||||
})
|
})
|
||||||
|
|
||||||
getPersons(newRelatiecodes)
|
userStore.getPersons(newRelatiecodes)
|
||||||
|
|
||||||
buttonsDisabled.value = false
|
buttonsDisabled.value = false
|
||||||
navigateTo('/settings')
|
navigateTo('/settings')
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCheckbox = (person) => {
|
const updateCheckbox = (person) => {
|
||||||
if (person.relatiecode === userPersons.value[0].relatiecode) return;
|
if (person.relatiecode === userStore.userPersons[0].relatiecode) return;
|
||||||
|
|
||||||
person.checked = !person.checked
|
person.checked = !person.checked
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllPersons = async () => {
|
|
||||||
if (userPersons.value.length === 0) return setTimeout(() => getAllPersons(), 50)
|
|
||||||
|
|
||||||
const idToken = await auth.value.currentUser.getIdToken(true)
|
|
||||||
|
|
||||||
|
|
||||||
const { data: response, error } = await useFetch('/api/getrelatiecodes', {
|
|
||||||
method: 'post',
|
|
||||||
body: { email: user.value.email, token: idToken }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (error.value) {
|
|
||||||
console.log(error.value)
|
|
||||||
return toast.error('Error tijdens het krijgen van relateicodes')
|
|
||||||
}
|
|
||||||
|
|
||||||
response.value.persons.forEach(person => {
|
|
||||||
if (userPersons.value.map(a => a.relatiecode).includes(person.relatiecode)) {
|
|
||||||
person.checked = true
|
|
||||||
} else {
|
|
||||||
person.checked = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
userAllPersons.value = response.value.persons
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -4,19 +4,19 @@
|
|||||||
<h1 class="text-xl ml-2 font-bold">Info</h1>
|
<h1 class="text-xl ml-2 font-bold">Info</h1>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="item">
|
<div class="item">
|
||||||
Email: <b>{{ user.email }}</b>
|
Email: <b>{{ userStore.user.email }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
<div class="item">
|
<div class="item">
|
||||||
Personen: <b>{{ userPersons.map(a => a.fullName).join(', ')}}</b>
|
Personen: <b>{{ userStore.userPersons.map(a => a.fullName).join(', ')}}</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
<div class="item">
|
<div class="item">
|
||||||
Groepen: <b>{{ groups.join(', ') }}</b>
|
Groepen: <b>{{ groups.join(', ') }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="userPersons.map(a => a.diploma).filter(n => n !== '').join('')" class="divider" />
|
<div v-if="userStore.userPersons.map(a => a.diploma).filter(n => n !== '').join('')" class="divider" />
|
||||||
<div v-if="userPersons.map(a => a.diploma).filter(n => n !== '').join('')" class="item">
|
<div v-if="userStore.userPersons.map(a => a.diploma).filter(n => n !== '').join('')" class="item">
|
||||||
Diploma: <b>{{ userPersons.map(a => a.diploma).filter(n => n !== '').join(', ')}}</b>
|
Diploma: <b>{{ userStore.userPersons.map(a => a.diploma).filter(n => n !== '').join(', ')}}</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
<NuxtLink to="/settings/moreinfo" class="item-hover py-2 rounded-t flex items-center">
|
<NuxtLink to="/settings/moreinfo" class="item-hover py-2 rounded-t flex items-center">
|
||||||
@ -43,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="userData.admin">
|
<div v-if="userStore.userData.admin">
|
||||||
<h1 class="text-xl ml-2 font-bold">Admin</h1>
|
<h1 class="text-xl ml-2 font-bold">Admin</h1>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<NuxtLink to="/settings/admin/users" class="rounded-t item-hover py-2 flex items-center">
|
<NuxtLink to="/settings/admin/users" class="rounded-t item-hover py-2 flex items-center">
|
||||||
@ -70,14 +70,14 @@ definePageMeta({
|
|||||||
title: 'Settings'
|
title: 'Settings'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { auth, userData, userPersons, user } = inject('firebase')
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const groups = computed(() => {
|
const groups = computed(() => {
|
||||||
return [...new Set(userPersons.value.map(a => a.groups.join()).join().split(','))]
|
return [...new Set(userStore.userPersons.map(a => a.groups.join()).join().split(','))]
|
||||||
})
|
})
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
signOut(auth.value)
|
signOut(userStore.auth)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
})
|
})
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
<h1 class="text-xl ml-2 font-bold"></h1>
|
<h1 class="text-xl ml-2 font-bold"></h1>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="item break-words ">
|
<div class="item break-words ">
|
||||||
Registration Token: <b>{{ registrationToken }}</b>
|
Registration Token: <b>{{ userStore.registrationToken }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
<div class="item break-words ">
|
<div class="item break-words ">
|
||||||
User ID: <b>{{ userData.id }}</b>
|
User ID: <b>{{ userStore.userData.id }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -28,9 +28,9 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
title: 'Meer Informatie',
|
title: 'Meer Informatie',
|
||||||
key: 'back'
|
key: 'back'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { registrationToken, userData } = inject('firebase')
|
const userStore = useUserStore()
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col gap-5 mx-auto p-2 w-full max-w-md text-default text-sm">
|
|
||||||
<h2 class="text-xl font-bold">
|
|
||||||
Privacy
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Gegevens binnen deze app worden gebruikt voor de interne organisatie. Via Google analytics wordt bijgehouden welke schermen het meest worden gebruikt. Daarnaast maken wij gebruik van Firebase voor het anoniem verzamelen van gegevens omtrent crashes, bugs en het gebruik van de app.
|
|
||||||
</p>
|
|
||||||
<h2 class="text-xl font-bold">
|
|
||||||
AVG
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Sinds 25 mei 2018 is de Algemene verordening gegevensbescherming (AVG) van toepassing waardoor elke vereniging helder moet maken wat zij doen om de privacy van persoonsgegevens te waarborgen. U kunt <a href="https://www.reddingsbrigadewaddinxveen.nl/Doc/201809%20-%20Privacyverklaring%20WRB.pdf" class="underline">hier</a> onze privacy verklaring vinden.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
definePageMeta({
|
|
||||||
title: 'Privacybeleid',
|
|
||||||
key: 'back'
|
|
||||||
})
|
|
||||||
</script>
|
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<label class="font-bold text-default">Deelnemer</label>
|
<label class="font-bold text-default">Deelnemer</label>
|
||||||
<select :disabled="modelData.edit" required v-model="modelData.relatiecode" class="input dark:bg-neutral-700 bg-neutral-300 mb-5">
|
<select :disabled="modelData.edit" required v-model="modelData.relatiecode" class="input dark:bg-neutral-700 bg-neutral-300 mb-5">
|
||||||
<option v-if="!modelData.edit" v-for="user in competitors.filter(x => !contest.events[modelData.event].competitors.map(y => y.relatiecode).includes(x.relatiecode))" :value="user.relatiecode">{{ user.name}} ({{ user.relatiecode }})</option>
|
<option v-if="!modelData.edit" v-for="user in contestStore.competitors.filter(x => !contest.events[modelData.event].competitors.map(y => y.relatiecode).includes(x.relatiecode))" :value="user.relatiecode">{{ user.name}} ({{ user.relatiecode }})</option>
|
||||||
<option v-else v-for="user in competitors" :value="user.relatiecode">{{ user.name}} ({{ user.relatiecode }})</option>
|
<option v-else v-for="user in competitors" :value="user.relatiecode">{{ user.name}} ({{ user.relatiecode }})</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
<label class="font-bold">Onderdelen</label>
|
<label class="font-bold">Onderdelen</label>
|
||||||
<div class="flex flex-col gap-y-3">
|
<div class="flex flex-col gap-y-3">
|
||||||
<div v-if="competitors" v-for="event in contest.events" class="container p-2">
|
<div v-if="contestStore.competitors[0]" v-for="event in contest.events" class="container p-2">
|
||||||
<div @click="event.open = !event.open" class="flex hover:cursor-pointer">
|
<div @click="event.open = !event.open" class="flex hover:cursor-pointer">
|
||||||
<h2 class="font-bold">{{ event.name }}</h2>
|
<h2 class="font-bold">{{ event.name }}</h2>
|
||||||
<Icon size="1.2em" name="ion:arrow-down-b" class="ml-auto my-auto mr-2 transition-all" :class="{'rotate-180' : event.open }" />
|
<Icon size="1.2em" name="ion:arrow-down-b" class="ml-auto my-auto mr-2 transition-all" :class="{'rotate-180' : event.open }" />
|
||||||
@ -61,11 +61,11 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr @click="handleModel(competitor, event.id, true, index)" v-for="(competitor, index) in event.competitors" class="even:dark:bg-neutral-700 even:bg-neutral-300 hover:cursor-pointer">
|
<tr @click="handleModel(competitor, event.id, true, index)" v-for="(competitor, index) in event.competitors" class="even:dark:bg-neutral-700 even:bg-neutral-300 hover:cursor-pointer">
|
||||||
<td class="py-1 pl-1">{{ competitors.find(x => x.relatiecode === competitor.relatiecode ).name }}</td>
|
<td class="py-1 pl-1">{{ contestStore.competitors.find(x => x.relatiecode === competitor.relatiecode ).name }}</td>
|
||||||
<td>{{ competitor.time.minutes.toString().padStart(2, '0') }}:{{ competitor.time.seconds.toString().padStart(2, '0') }}:{{ competitor.time.milliseconds.toString().padStart(2, '0') }}</td>
|
<td>{{ competitor.time.minutes.toString().padStart(2, '0') }}:{{ competitor.time.seconds.toString().padStart(2, '0') }}:{{ competitor.time.milliseconds.toString().padStart(2, '0') }}</td>
|
||||||
<td>{{ competitor.dsq }}</td>
|
<td>{{ competitor.dsq }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="competitors.filter(x => !event.competitors.map(y => y.relatiecode).includes(x.relatiecode)).length > 0" class="even:dark:bg-neutral-700 even:bg-neutral-300">
|
<tr v-if="contestStore.competitors.filter(x => !event.competitors.map(y => y.relatiecode).includes(x.relatiecode)).length > 0" class="even:dark:bg-neutral-700 even:bg-neutral-300">
|
||||||
<td @click="handleModel(null, event.id)" class="hover:cursor-pointer py-1 pl-1">+ Deelnemer toevoegen</td>
|
<td @click="handleModel(null, event.id)" class="hover:cursor-pointer py-1 pl-1">+ Deelnemer toevoegen</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
@ -81,7 +81,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getDocs, collection, writeBatch, doc } from "firebase/firestore"
|
import { getDocs, collection, writeBatch, doc, getFirestore } from "firebase/firestore"
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@ -91,7 +91,9 @@ definePageMeta({
|
|||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
const { db, competitors } = inject('firebase')
|
const contestStore = useContestStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const db = getFirestore()
|
||||||
|
|
||||||
const showModel = ref(false)
|
const showModel = ref(false)
|
||||||
const disableButtons = ref(false)
|
const disableButtons = ref(false)
|
||||||
@ -151,14 +153,6 @@ const contest = ref({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const getCompetitors = async () => {
|
|
||||||
if (competitors.value[0]) return
|
|
||||||
const querySnapshot = await getDocs(collection(db, "competitors"))
|
|
||||||
querySnapshot.forEach((doc) => {
|
|
||||||
competitors.value.push(doc.data())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleModel = (competitor, event, edit, index) => {
|
const handleModel = (competitor, event, edit, index) => {
|
||||||
if(!competitor) competitor = {
|
if(!competitor) competitor = {
|
||||||
relatiecode: '',
|
relatiecode: '',
|
||||||
@ -205,7 +199,7 @@ const submitContestForm = async () => {
|
|||||||
const docRef = doc(collection(db, 'timings'))
|
const docRef = doc(collection(db, 'timings'))
|
||||||
batch.set(docRef, {
|
batch.set(docRef, {
|
||||||
relatiecode: competitor.relatiecode,
|
relatiecode: competitor.relatiecode,
|
||||||
contest: { location: contest.value.location, date: contest.value.date.toString(), type: contest.value.type },
|
contest: { location: contest.value.location, date: new Date(contest.value.date), type: contest.value.type },
|
||||||
event: event.id,
|
event: event.id,
|
||||||
time: { minutes: competitor.time.minutes.toString().padStart(2, '0'), seconds: competitor.time.seconds.toString().padStart(2, '0'), milliseconds: competitor.time.milliseconds.toString().padStart(2, '0'), combined: combinedTime },
|
time: { minutes: competitor.time.minutes.toString().padStart(2, '0'), seconds: competitor.time.seconds.toString().padStart(2, '0'), milliseconds: competitor.time.milliseconds.toString().padStart(2, '0'), combined: combinedTime },
|
||||||
dsq: competitor.dsq,
|
dsq: competitor.dsq,
|
||||||
@ -222,7 +216,7 @@ const submitContestForm = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getCompetitors()
|
contestStore.getCompetitors()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -4,44 +4,44 @@
|
|||||||
<h1 class="font-bold text-center text-lg mb-5">Tijd {{ modelData.eventName }}</h1>
|
<h1 class="font-bold text-center text-lg mb-5">Tijd {{ modelData.eventName }}</h1>
|
||||||
|
|
||||||
<label class="font-bold">Locatie</label>
|
<label class="font-bold">Locatie</label>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.contest.location" type="text" class="input dark:bg-neutral-700 bg-neutral-300 mb-5" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.contest.location" type="text" class="input dark:bg-neutral-700 bg-neutral-300 mb-5" />
|
||||||
|
|
||||||
<label class="font-bold">Type zwembad</label>
|
<label class="font-bold">Type zwembad</label>
|
||||||
<select :disabled="!userData.wedstrijdAdmin" v-model="modelData.contest.type" required="true" class="input dark:bg-neutral-700 bg-neutral-300 mb-5">
|
<select :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.contest.type" required="true" class="input dark:bg-neutral-700 bg-neutral-300 mb-5">
|
||||||
<option value="50m">50 Meter</option>
|
<option value="50m">50 Meter</option>
|
||||||
<option value="25m">25 Meter</option>
|
<option value="25m">25 Meter</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label class="font-bold">Datum</label>
|
<label class="font-bold">Datum</label>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" :value="dateToYYYYMMDD(modelData.contest.date)" @input="modelData.contest.date = $event.target.valueAsDate" required="true" class="input dark:bg-neutral-700 bg-neutral-300 w-min hover:cursor-pointer pr-0 mb-5 " type="date">
|
<input :disabled="!userStore.userData.wedstrijdAdmin" :value="dateToYYYYMMDD(modelData.contest.date)" @input="modelData.contest.date = $event.target.valueAsDate" required="true" class="input dark:bg-neutral-700 bg-neutral-300 w-min hover:cursor-pointer pr-0 mb-5 " type="date">
|
||||||
|
|
||||||
<label class="font-bold">Tijd</label>
|
<label class="font-bold">Tijd</label>
|
||||||
<div class="mb-1">
|
<div class="mb-1">
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.time.minutes" type="number" step="1" min="0" max="99" placeholder="mm" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.time.minutes" type="number" step="1" min="0" max="99" placeholder="mm" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
||||||
<span class="text-default text-xl font-bold mx-1">:</span>
|
<span class="text-default text-xl font-bold mx-1">:</span>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.time.seconds" type="number" step="1" min="0" max="99" placeholder="ss" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.time.seconds" type="number" step="1" min="0" max="99" placeholder="ss" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
||||||
<span class="text-default text-xl font-bold mx-1">:</span>
|
<span class="text-default text-xl font-bold mx-1">:</span>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.time.milliseconds" type="number" step="1" min="0" max="99" placeholder="ms" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.time.milliseconds" type="number" step="1" min="0" max="99" placeholder="ms" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center mb-5">
|
<div class="flex items-center mb-5">
|
||||||
<input :disabled="!userData.wedstrijdAdmin" type="checkbox" v-model="modelData.dsq" class="mr-1 checkbox">
|
<input :disabled="!userStore.userData.wedstrijdAdmin" type="checkbox" v-model="modelData.dsq" class="mr-1 checkbox">
|
||||||
<span class="text-default">Diskwalificatie</span>
|
<span class="text-default">Diskwalificatie</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="font-bold">Info </label>
|
<label class="font-bold">Info </label>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.info" type="text" placeholder="Bijv. Een diskwalificatie" class="input dark:bg-neutral-700 bg-neutral-300 mb-10" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.info" type="text" placeholder="Bijv. Een diskwalificatie" class="input dark:bg-neutral-700 bg-neutral-300 mb-10" />
|
||||||
|
|
||||||
<input v-if="userData.wedstrijdAdmin" :disabled="disableButtons" type="submit" class="btn" value="Bewerken" />
|
<input v-if="userStore.userData.wedstrijdAdmin" :disabled="disableButtons" type="submit" class="btn" value="Bewerken" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="timings[0]" class="flex flex-col justify-center items-center gap-y-3 px-2 overflow-hidden">
|
<div v-if="contestStore.timings[0]" class="flex flex-col justify-center items-center gap-y-3 px-2 overflow-hidden">
|
||||||
<div class="flex gap-x-5">
|
<div class="flex gap-x-5">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<button @click.stop="showDeelnemersDropdown = !showDeelnemersDropdown" class="btn">Deelnemers <Icon size="1.2em" name="ion:arrow-down-b" /></button>
|
<button @click.stop="showDeelnemersDropdown = !showDeelnemersDropdown" class="btn">Deelnemers <Icon size="1.2em" name="ion:arrow-down-b" /></button>
|
||||||
<div v-if="showDeelnemersDropdown" v-on-click-outside.bubble="handleDeelnemersDropdown" class="w-48 mt-2 container absolute rounded-lg shadow p-3">
|
<div v-if="showDeelnemersDropdown" v-on-click-outside.bubble="handleDeelnemersDropdown" class="w-48 mt-2 container absolute rounded-lg shadow p-3">
|
||||||
<ul class="space-y-2 text-default">
|
<ul class="space-y-2 text-default">
|
||||||
<li v-for="competitor in competitors" @click="competitor.checked = !competitor.checked" class="flex gap-x-1 items-center hover:cursor-pointer">
|
<li v-for="competitor in contestStore.competitors" @click="competitor.checked = !competitor.checked" class="flex gap-x-1 items-center hover:cursor-pointer">
|
||||||
<input v-model="competitor.checked" type="checkbox" class="checkbox">
|
<input v-model="competitor.checked" type="checkbox" class="checkbox">
|
||||||
<label class="hover:cursor-pointer">{{ competitor.name }}</label>
|
<label class="hover:cursor-pointer">{{ competitor.name }}</label>
|
||||||
</li>
|
</li>
|
||||||
@ -63,14 +63,14 @@
|
|||||||
<div v-for="event in events" class="container w-full max-w-3xl py-2 px-4">
|
<div v-for="event in events" class="container w-full max-w-3xl py-2 px-4">
|
||||||
<div @click="event.open = !event.open" class="flex hover:cursor-pointer">
|
<div @click="event.open = !event.open" class="flex hover:cursor-pointer">
|
||||||
<h2 class="font-bold mr-auto">{{ event.name }}</h2>
|
<h2 class="font-bold mr-auto">{{ event.name }}</h2>
|
||||||
<span v-if="filteredTimings.filter(a => a.event === event.id).length > 0" class="">
|
<span v-if="contestStore.filteredTimings.filter(a => a.event === event.id).length > 0" class="">
|
||||||
<span class="hidden md:inline-block mr-1">
|
<span class="hidden md:inline-block mr-1">
|
||||||
{{ filteredTimings.filter(a => a.event === event.id)[0].contest.date.toLocaleDateString('nl-NL') }} |
|
{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].contest.date.toLocaleDateString('nl-NL') }} |
|
||||||
{{ filteredTimings.filter(a => a.event === event.id)[0].contest.location }} |
|
{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].contest.location }} |
|
||||||
{{ filteredTimings.filter(a => a.event === event.id)[0].contest.type }} |
|
{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].contest.type }} |
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{{ filteredTimings.filter(a => a.event === event.id)[0].time.minutes }}:{{ filteredTimings.filter(a => a.event === event.id)[0].time.seconds }}:{{ filteredTimings.filter(a => a.event === event.id)[0].time.milliseconds }}</span>
|
{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].time.minutes }}:{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].time.seconds }}:{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].time.milliseconds }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="">Geen tijd</span>
|
<span v-else class="">Geen tijd</span>
|
||||||
<Icon size="1.2em" name="ion:arrow-down-b" class="my-auto ml-2 transition-all" :class="{'rotate-180' : event.open }" />
|
<Icon size="1.2em" name="ion:arrow-down-b" class="my-auto ml-2 transition-all" :class="{'rotate-180' : event.open }" />
|
||||||
@ -83,10 +83,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr @click="handleModel(time, event, index)" v-for="(time, index) in filteredTimings.filter(a => a.event === event.id)" class="even:dark:bg-neutral-700 even:bg-neutral-300 hover:cursor-pointer">
|
<tr @click="handleModel(time, event, index)" v-for="(time, index) in contestStore.filteredTimings.filter(a => a.event === event.id)" class="even:dark:bg-neutral-700 even:bg-neutral-300 hover:cursor-pointer">
|
||||||
<td v-if="properties.time.enabled" class="pl-1" :class="time.dsq ? 'line-through' : ''">{{ time.time.minutes }}:{{ time.time.seconds }}:{{ time.time.milliseconds}}</td>
|
<td v-if="properties.time.enabled" class="pl-1" :class="time.dsq ? 'line-through' : ''">{{ time.time.minutes }}:{{ time.time.seconds }}:{{ time.time.milliseconds}}</td>
|
||||||
<td v-if="properties.date.enabled">{{ time.contest.date.toLocaleDateString('nl-NL') }}</td>
|
<td v-if="properties.date.enabled">{{ time.contest.date.toLocaleDateString('nl-NL') }}</td>
|
||||||
<td v-if="properties.name.enabled" class="overflow-hidden whitespace-nowrap truncate">{{ competitors.filter(a => a.relatiecode === time.relatiecode)[0].name.split(', ')[1] + ' ' + competitors.filter(a => a.relatiecode === time.relatiecode)[0].name.split(', ')[0] }}</td>
|
<td v-if="properties.name.enabled" class="overflow-hidden whitespace-nowrap truncate">{{ contestStore.competitors.filter(a => a.relatiecode === time.relatiecode)[0].name.split(', ')[1] + ' ' + contestStore.competitors.filter(a => a.relatiecode === time.relatiecode)[0].name.split(', ')[0] }}</td>
|
||||||
<td v-if="properties.type.enabled">{{ time.contest.type }}</td>
|
<td v-if="properties.type.enabled">{{ time.contest.type }}</td>
|
||||||
<td v-if="properties.location.enabled">{{ time.contest.location }}</td>
|
<td v-if="properties.location.enabled">{{ time.contest.location }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -107,10 +107,10 @@ definePageMeta({
|
|||||||
key: 'back'
|
key: 'back'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { db, userData, competitors } = inject('firebase')
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const contestStore = useContestStore()
|
||||||
|
|
||||||
const timings = ref([])
|
|
||||||
const showModel = ref(false)
|
const showModel = ref(false)
|
||||||
const disableButtons = ref(false)
|
const disableButtons = ref(false)
|
||||||
const showDeelnemersDropdown = ref(false)
|
const showDeelnemersDropdown = ref(false)
|
||||||
@ -124,16 +124,6 @@ const handlePropertiesDropdown = () => {
|
|||||||
showPropertiesDropdown.value = false
|
showPropertiesDropdown.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredTimings = computed(() => {
|
|
||||||
timings.value = timings.value.sort((a, b) => a.time.combined.localeCompare(b.time.combined))
|
|
||||||
timings.value.forEach((time, index) => {
|
|
||||||
if (time.dsq === true) {
|
|
||||||
timings.value.push(timings.value.splice(index, 1)[0])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return timings.value.filter(a => competitors.value.filter(b => b.checked === true).map(x => x.relatiecode).includes(a.relatiecode))
|
|
||||||
})
|
|
||||||
|
|
||||||
const modelData = ref({
|
const modelData = ref({
|
||||||
time: {
|
time: {
|
||||||
minutes: null,
|
minutes: null,
|
||||||
@ -216,35 +206,11 @@ const events = ref({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const getCompetitors = async () => {
|
|
||||||
if (competitors.value[0]) return
|
|
||||||
const querySnapshot = await getDocs(collection(db, "competitors"))
|
|
||||||
querySnapshot.forEach((doc) => {
|
|
||||||
const data = doc.data()
|
|
||||||
data.checked = true
|
|
||||||
competitors.value.push(data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const citiesRef = collection(db, "timings");
|
await contestStore.getCompetitors()
|
||||||
|
await contestStore.getTimings()
|
||||||
|
|
||||||
const q = query(citiesRef);
|
contestStore.selectCompetitors('all')
|
||||||
|
|
||||||
const querySnapshot = await getDocs(q);
|
|
||||||
|
|
||||||
querySnapshot.forEach((doc) => {
|
|
||||||
// doc.data() is never undefined for query doc snapshots
|
|
||||||
const data = doc.data()
|
|
||||||
data.id = doc.id
|
|
||||||
|
|
||||||
data.contest.date = data.contest.date.toDate()
|
|
||||||
|
|
||||||
timings.value.push(data)
|
|
||||||
});
|
|
||||||
|
|
||||||
timings.value = timings.value.sort((a, b) => a.time.combined.localeCompare(b.time.combined))
|
|
||||||
getCompetitors()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const dateToYYYYMMDD = (d) => {
|
const dateToYYYYMMDD = (d) => {
|
||||||
|
@ -10,9 +10,9 @@
|
|||||||
<span>Brigade Tijden</span>
|
<span>Brigade Tijden</span>
|
||||||
<Icon class="ml-auto" size="2em" name="ion:arrow-forward"/>
|
<Icon class="ml-auto" size="2em" name="ion:arrow-forward"/>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<div v-if="userData.wedstrijdAdmin" class="divider" />
|
<div v-if="userStore.userData.wedstrijdAdmin" class="divider" />
|
||||||
<NuxtLink v-if="userData.wedstrijdAdmin" to="/wedstrijd/addcontest" class="rounded-b item-hover py-2 flex items-center">
|
<NuxtLink v-if="userStore.userData.wedstrijdAdmin" to="/wedstrijd/addcontest" class="rounded-b item-hover py-2 flex items-center">
|
||||||
<span>Wedstrijd Toevoegen</span>
|
<span>Tijden Toevoegen</span>
|
||||||
<Icon class="ml-auto" size="2em" name="ion:arrow-forward"/>
|
<Icon class="ml-auto" size="2em" name="ion:arrow-forward"/>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
@ -24,5 +24,5 @@ definePageMeta({
|
|||||||
title: 'Wedstrijd'
|
title: 'Wedstrijd'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { userData } = inject('firebase')
|
const userStore = useUserStore()
|
||||||
</script>
|
</script>
|
||||||
|
@ -4,42 +4,42 @@
|
|||||||
<h1 class="font-bold text-center text-lg mb-5">Tijd {{ modelData.eventName }}</h1>
|
<h1 class="font-bold text-center text-lg mb-5">Tijd {{ modelData.eventName }}</h1>
|
||||||
|
|
||||||
<label class="font-bold">Locatie</label>
|
<label class="font-bold">Locatie</label>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.contest.location" type="text" class="input dark:bg-neutral-700 bg-neutral-300 mb-5" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.contest.location" type="text" class="input dark:bg-neutral-700 bg-neutral-300 mb-5" />
|
||||||
|
|
||||||
<label class="font-bold">Datum</label>
|
<label class="font-bold">Datum</label>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" :value="dateToYYYYMMDD(modelData.contest.date)" @input="modelData.contest.date = $event.target.valueAsDate" required="true" class="input dark:bg-neutral-700 bg-neutral-300 w-min hover:cursor-pointer pr-0 mb-5 " type="date">
|
<input :disabled="!userStore.userData.wedstrijdAdmin" :value="dateToYYYYMMDD(modelData.contest.date)" @input="modelData.contest.date = $event.target.valueAsDate" required="true" class="input dark:bg-neutral-700 bg-neutral-300 w-min hover:cursor-pointer pr-0 mb-5 " type="date">
|
||||||
|
|
||||||
<label class="font-bold">Type zwembad</label>
|
<label class="font-bold">Type zwembad</label>
|
||||||
<select :disabled="!userData.wedstrijdAdmin" v-model="modelData.contest.type" required="true" class="input dark:bg-neutral-700 bg-neutral-300 mb-5">
|
<select :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.contest.type" required="true" class="input dark:bg-neutral-700 bg-neutral-300 mb-5">
|
||||||
<option value="50m">50 Meter</option>
|
<option value="50m">50 Meter</option>
|
||||||
<option value="25m">25 Meter</option>
|
<option value="25m">25 Meter</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label class="font-bold">Tijd</label>
|
<label class="font-bold">Tijd</label>
|
||||||
<div class="mb-1">
|
<div class="mb-1">
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.time.minutes" type="number" step="1" min="0" max="99" placeholder="mm" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.time.minutes" type="number" step="1" min="0" max="99" placeholder="mm" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
||||||
<span class="text-default text-xl font-bold mx-1">:</span>
|
<span class="text-default text-xl font-bold mx-1">:</span>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.time.seconds" type="number" step="1" min="0" max="99" placeholder="ss" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.time.seconds" type="number" step="1" min="0" max="99" placeholder="ss" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
||||||
<span class="text-default text-xl font-bold mx-1">:</span>
|
<span class="text-default text-xl font-bold mx-1">:</span>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.time.milliseconds" type="number" step="1" min="0" max="99" placeholder="ms" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.time.milliseconds" type="number" step="1" min="0" max="99" placeholder="ms" class="input dark:bg-neutral-700 bg-neutral-300 w-10 text-center p-1" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center mb-5">
|
<div class="flex items-center mb-5">
|
||||||
<input :disabled="!userData.wedstrijdAdmin" type="checkbox" v-model="modelData.dsq" class="mr-1 checkbox">
|
<input :disabled="!userStore.userData.wedstrijdAdmin" type="checkbox" v-model="modelData.dsq" class="mr-1 checkbox">
|
||||||
<span class="text-default">Diskwalificatie</span>
|
<span class="text-default">Diskwalificatie</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="font-bold">Info </label>
|
<label class="font-bold">Info </label>
|
||||||
<input :disabled="!userData.wedstrijdAdmin" v-model="modelData.info" type="text" placeholder="Bijv. Een diskwalificatie" class="input dark:bg-neutral-700 bg-neutral-300 mb-10" />
|
<input :disabled="!userStore.userData.wedstrijdAdmin" v-model="modelData.info" type="text" placeholder="Bijv. Een diskwalificatie" class="input dark:bg-neutral-700 bg-neutral-300 mb-10" />
|
||||||
|
|
||||||
<input v-if="userData.wedstrijdAdmin" :disabled="disableButtons" type="submit" class="btn" value="Bewerken" />
|
<input v-if="userStore.userData.wedstrijdAdmin" :disabled="disableButtons" type="submit" class="btn" value="Bewerken" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="timings[0]" class="flex flex-col justify-center items-center gap-y-3 px-2 overflow-hidden">
|
<div v-if="contestStore.filteredTimings[0]" class="flex flex-col justify-center items-center gap-y-3 px-2 overflow-hidden">
|
||||||
<div v-for="event in events" class="container w-full max-w-md py-2 px-4">
|
<div v-for="event in events" class="container w-full max-w-md py-2 px-4">
|
||||||
<div @click="event.open = !event.open" class="flex hover:cursor-pointer">
|
<div @click="event.open = !event.open" class="flex hover:cursor-pointer">
|
||||||
<h2 class="font-bold">{{ event.name }}</h2>
|
<h2 class="font-bold">{{ event.name }}</h2>
|
||||||
<span v-if="timings.filter(a => a.event === event.id).length > 0" class="ml-auto">{{ filteredTimings.filter(a => a.event === event.id)[0].time.minutes }}:{{ filteredTimings.filter(a => a.event === event.id)[0].time.seconds }}:{{ filteredTimings.filter(a => a.event === event.id)[0].time.milliseconds }}</span>
|
<span v-if="contestStore.filteredTimings.filter(a => a.event === event.id)[0]" class="ml-auto">{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].time.minutes }}:{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].time.seconds }}:{{ contestStore.filteredTimings.filter(a => a.event === event.id)[0].time.milliseconds }}</span>
|
||||||
<span v-else class="ml-auto">Geen tijd</span>
|
<span v-else class="ml-auto">Geen tijd</span>
|
||||||
<Icon size="1.2em" name="ion:arrow-down-b" class="my-auto ml-2 transition-all" :class="{'rotate-180' : event.open }" />
|
<Icon size="1.2em" name="ion:arrow-down-b" class="my-auto ml-2 transition-all" :class="{'rotate-180' : event.open }" />
|
||||||
</div>
|
</div>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr @click="handleModel(time, event, index)" v-for="(time, index) in filteredTimings.filter(a => a.event === event.id)" class="even:dark:bg-neutral-700 even:bg-neutral-300 hover:cursor-pointer">
|
<tr @click="handleModel(time, event, index)" v-for="(time, index) in contestStore.filteredTimings.filter(a => a.event === event.id)" class="even:dark:bg-neutral-700 even:bg-neutral-300 hover:cursor-pointer">
|
||||||
<td class="pl-1" :class="time.dsq ? 'line-through' : ''">{{ time.time.minutes }}:{{ time.time.seconds }}:{{ time.time.milliseconds}}</td>
|
<td class="pl-1" :class="time.dsq ? 'line-through' : ''">{{ time.time.minutes }}:{{ time.time.seconds }}:{{ time.time.milliseconds}}</td>
|
||||||
<td>{{ time.contest.date.toLocaleDateString('nl-NL') }}</td>
|
<td>{{ time.contest.date.toLocaleDateString('nl-NL') }}</td>
|
||||||
<td>{{ time.contest.type }}</td>
|
<td>{{ time.contest.type }}</td>
|
||||||
@ -74,23 +74,13 @@ definePageMeta({
|
|||||||
key: 'back'
|
key: 'back'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { db, userData } = inject('firebase')
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const contestStore = useContestStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const timings = ref([])
|
|
||||||
const showModel = ref(false)
|
const showModel = ref(false)
|
||||||
const disableButtons = ref(false)
|
const disableButtons = ref(false)
|
||||||
|
|
||||||
const filteredTimings = computed(() => {
|
|
||||||
timings.value = timings.value.sort((a, b) => a.time.combined.localeCompare(b.time.combined))
|
|
||||||
timings.value.forEach((time, index) => {
|
|
||||||
if (time.dsq === true) {
|
|
||||||
timings.value.push(timings.value.splice(index, 1)[0])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return timings.value
|
|
||||||
})
|
|
||||||
|
|
||||||
const modelData = ref({
|
const modelData = ref({
|
||||||
time: {
|
time: {
|
||||||
minutes: null,
|
minutes: null,
|
||||||
@ -146,23 +136,8 @@ const events = ref({
|
|||||||
})
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const citiesRef = collection(db, "timings");
|
await contestStore.getTimings()
|
||||||
|
contestStore.selectCompetitors('user', userStore.userData.relatiecodes)
|
||||||
const q = query(citiesRef, where("relatiecode", "in", userData.value.relatiecodes ));
|
|
||||||
|
|
||||||
const querySnapshot = await getDocs(q);
|
|
||||||
|
|
||||||
querySnapshot.forEach((doc) => {
|
|
||||||
// doc.data() is never undefined for query doc snapshots
|
|
||||||
const data = doc.data()
|
|
||||||
data.id = doc.id
|
|
||||||
|
|
||||||
data.contest.date = data.contest.date.toDate()
|
|
||||||
|
|
||||||
timings.value.push(data)
|
|
||||||
});
|
|
||||||
|
|
||||||
timings.value = timings.value.sort((a, b) => a.time.combined.localeCompare(b.time.combined))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
52
frontend/stores/calendarStore.js
Normal file
52
frontend/stores/calendarStore.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
export const useCalendarStore = defineStore('calendar', () => {
|
||||||
|
const events = ref([])
|
||||||
|
|
||||||
|
const getEvents = async (group) => {
|
||||||
|
let fridayEvents = []
|
||||||
|
let saturdayEvents = []
|
||||||
|
let matchEvents = []
|
||||||
|
|
||||||
|
if (group.includes('Zaterdag')){
|
||||||
|
let data = await fetch('https://www.googleapis.com/calendar/v3/calendars/c_astg0d0auheip5o4269v1qvv3g@group.calendar.google.com/events?key=AIzaSyBLmxWNEnbkiW_0c2UrsHMbxXv73dX-KYw')
|
||||||
|
if (!data.ok){
|
||||||
|
throw Error('No data available')
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonEvents = await data.json()
|
||||||
|
saturdayEvents = jsonEvents.items
|
||||||
|
} if (group.includes('Vrijdag')){
|
||||||
|
let data = await fetch('https://www.googleapis.com/calendar/v3/calendars/c_u3895n01jt8qnusm7f6pmqjb0k%40group.calendar.google.com/events?key=AIzaSyBLmxWNEnbkiW_0c2UrsHMbxXv73dX-KYw')
|
||||||
|
if (!data.ok){
|
||||||
|
throw Error('No data available')
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonEvents = await data.json()
|
||||||
|
fridayEvents = jsonEvents.items
|
||||||
|
} if (group.includes('Wedstrijd')){
|
||||||
|
let data = await fetch('https://www.googleapis.com/calendar/v3/calendars/c_c2296iboq07n24galuobeesovs@group.calendar.google.com/events?key=AIzaSyBLmxWNEnbkiW_0c2UrsHMbxXv73dX-KYw')
|
||||||
|
if (!data.ok){
|
||||||
|
throw Error('No data available')
|
||||||
|
}
|
||||||
|
const jsonEvents = await data.json()
|
||||||
|
matchEvents = jsonEvents.items
|
||||||
|
}
|
||||||
|
|
||||||
|
const allEvents = [...fridayEvents, ...saturdayEvents, ...matchEvents]
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
now.setHours(0, 0, 0, 0)
|
||||||
|
|
||||||
|
const sortedEvents = allEvents.sort((a, b) => new Date(a.start.dateTime) - new Date(b.start.dateTime))
|
||||||
|
const filteredEvents = sortedEvents.filter((event) => new Date(event.start.dateTime) > now )
|
||||||
|
|
||||||
|
filteredEvents.forEach(element => {
|
||||||
|
events.value.push({
|
||||||
|
description: element.description,
|
||||||
|
date: new Date(element.start.dateTime)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { events, getEvents }
|
||||||
|
})
|
||||||
|
|
74
frontend/stores/contestStore.js
Normal file
74
frontend/stores/contestStore.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { getDocs, collection, getFirestore, query } from 'firebase/firestore'
|
||||||
|
|
||||||
|
export const useContestStore = defineStore('contest', () => {
|
||||||
|
// General
|
||||||
|
const db = getFirestore()
|
||||||
|
|
||||||
|
// Contest competitors
|
||||||
|
const competitors = ref([])
|
||||||
|
|
||||||
|
const getCompetitors = async () => {
|
||||||
|
if (competitors.value[0]) return
|
||||||
|
|
||||||
|
const querySnapshot = await getDocs(collection(db, "competitors"))
|
||||||
|
|
||||||
|
querySnapshot.forEach((doc) => {
|
||||||
|
const data = doc.data()
|
||||||
|
data.checked = true
|
||||||
|
competitors.value.push(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectCompetitors = (x, relatiecodes) => {
|
||||||
|
if (x === 'user') {
|
||||||
|
competitors.value.forEach(competitor => {
|
||||||
|
if (relatiecodes.includes(competitor.relatiecode)) {
|
||||||
|
competitor.checked = true
|
||||||
|
} else {
|
||||||
|
competitor.checked = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (x === 'all') {
|
||||||
|
competitors.value.forEach(competitor => competitor.checked = true)
|
||||||
|
} else if (x === 'none') {
|
||||||
|
competitors.value.forEach(competitor => competitor.checked = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contest Timings
|
||||||
|
const timings = ref([])
|
||||||
|
|
||||||
|
const filteredTimings = computed(() => {
|
||||||
|
timings.value = timings.value.sort((a, b) => a.time.combined.localeCompare(b.time.combined))
|
||||||
|
|
||||||
|
timings.value.forEach((time, index) => {
|
||||||
|
if (time.dsq === true) {
|
||||||
|
timings.value.push(timings.value.splice(index, 1)[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return timings.value.filter(a => competitors.value.filter(b => b.checked === true).map(x => x.relatiecode).includes(a.relatiecode))
|
||||||
|
})
|
||||||
|
|
||||||
|
const getTimings = async () => {
|
||||||
|
if (timings.value[0]) return;
|
||||||
|
if (!competitors.value[0]) await getCompetitors()
|
||||||
|
|
||||||
|
const timingsRef = collection(db, "timings");
|
||||||
|
|
||||||
|
const q = query(timingsRef);
|
||||||
|
|
||||||
|
const querySnapshot = await getDocs(q);
|
||||||
|
|
||||||
|
querySnapshot.forEach((doc) => {
|
||||||
|
const data = doc.data()
|
||||||
|
data.id = doc.id
|
||||||
|
|
||||||
|
data.contest.date = data.contest.date.toDate()
|
||||||
|
|
||||||
|
timings.value.push(data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { competitors, getCompetitors, getTimings, timings, filteredTimings, selectCompetitors }
|
||||||
|
})
|
80
frontend/stores/newsStore.js
Normal file
80
frontend/stores/newsStore.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { getDocs, collection, getFirestore, deleteDoc, doc, serverTimestamp, Timestamp, addDoc } from 'firebase/firestore'
|
||||||
|
import { getAuth } from 'firebase/auth'
|
||||||
|
|
||||||
|
export const useNewsStore = defineStore('news', () => {
|
||||||
|
const db = getFirestore()
|
||||||
|
const auth = getAuth()
|
||||||
|
|
||||||
|
const news = ref([])
|
||||||
|
const loaded = ref(false)
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const getNews = async () => {
|
||||||
|
news.value = []
|
||||||
|
try {
|
||||||
|
const querySnapshot = await getDocs(collection(db, "news"));
|
||||||
|
querySnapshot.forEach((doc) => {
|
||||||
|
let data = doc.data()
|
||||||
|
data.id = doc.id
|
||||||
|
news.value.push(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
news.value.sort((a, b) => b.date.seconds - a.date.seconds)
|
||||||
|
|
||||||
|
loaded.value = true
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteNews = async (item, index) => {
|
||||||
|
if (!item.id) return toast.error('Refresh eerst voordat je dit bericht kan verwijderen')
|
||||||
|
|
||||||
|
try {
|
||||||
|
await deleteDoc(doc(db, "news", item.id));
|
||||||
|
|
||||||
|
news.value.splice(index, 1)
|
||||||
|
toast.success('Bericht verwijderd')
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
|
||||||
|
toast.error('Error tijdens bericht verwijderen')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const send = async (form) => {
|
||||||
|
const idToken = await auth.currentUser.getIdToken(true)
|
||||||
|
console.log(idToken)
|
||||||
|
|
||||||
|
const { error } = await useFetch('/api/sendmessage', {
|
||||||
|
method: 'post',
|
||||||
|
body: { title: form.title, body: form.description, token: idToken, topic: form.topic }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.log(error.value)
|
||||||
|
return toast.error('Error tijdens het versturen van het bericht')
|
||||||
|
}
|
||||||
|
|
||||||
|
await addDoc(collection(db, "news"), {
|
||||||
|
title: form.title,
|
||||||
|
description: form.description,
|
||||||
|
date: serverTimestamp()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (news.value) {
|
||||||
|
news.value.unshift({
|
||||||
|
title: form.title,
|
||||||
|
description: form.description,
|
||||||
|
date: Timestamp.now()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
toast.success('Bericht is verstuurd')
|
||||||
|
|
||||||
|
navigateTo('/news')
|
||||||
|
}
|
||||||
|
|
||||||
|
return { getNews, news, loaded, deleteNews, send }
|
||||||
|
});
|
93
frontend/stores/userStore.js
Normal file
93
frontend/stores/userStore.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { onAuthStateChanged, getAuth } from 'firebase/auth'
|
||||||
|
import { getDoc, doc, getFirestore } from 'firebase/firestore'
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('user', () => {
|
||||||
|
const db = getFirestore()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const auth = ref(null)
|
||||||
|
auth.value = getAuth()
|
||||||
|
|
||||||
|
const user = ref(null)
|
||||||
|
const isAuthenticated = ref(false)
|
||||||
|
const userData = ref(false)
|
||||||
|
const userPersons = ref([])
|
||||||
|
const userLoaded = ref(false)
|
||||||
|
const registrationToken = ref('')
|
||||||
|
const userAllPersons = ref([])
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
onAuthStateChanged(auth.value, async (usr) => {
|
||||||
|
if (usr) {
|
||||||
|
user.value = usr
|
||||||
|
|
||||||
|
let docRef = doc(db, "users", user.value.uid);
|
||||||
|
let docSnap = await getDoc(docRef);
|
||||||
|
|
||||||
|
if (docSnap.exists()) {
|
||||||
|
const data = docSnap.data()
|
||||||
|
userData.value = data
|
||||||
|
getPersons(userData.value.relatiecodes)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => window.location.reload(true), 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userData.value.sendNews && route.path === '/news/newmessage') navigateTo('/')
|
||||||
|
if (!userData.value.admin && route.path.startsWith('/settings/admin')) navigateTo('/')
|
||||||
|
|
||||||
|
isAuthenticated.value = true
|
||||||
|
|
||||||
|
setupNotifications()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
isAuthenticated.value = false
|
||||||
|
user.value = null
|
||||||
|
userData.value = null
|
||||||
|
userPersons.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
userLoaded.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPersons = async (persons) => {
|
||||||
|
userPersons.value = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < persons.length; i++) {
|
||||||
|
const docRef = doc(db, "ledenlijst", persons[i]);
|
||||||
|
const docSnap = await getDoc(docRef);
|
||||||
|
|
||||||
|
if (docSnap.exists()) {
|
||||||
|
userPersons.value.push(docSnap.data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllPersons = async () => {
|
||||||
|
if (userPersons.value.length === 0) return setTimeout(() => getAllPersons(), 50)
|
||||||
|
|
||||||
|
const idToken = await auth.value.currentUser.getIdToken(true)
|
||||||
|
|
||||||
|
const { data: response, error } = await useFetch('/api/getrelatiecodes', {
|
||||||
|
method: 'post',
|
||||||
|
body: { email: user.value.email, token: idToken }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.log(error.value)
|
||||||
|
return toast.error('Error tijdens het krijgen van relateicodes')
|
||||||
|
}
|
||||||
|
|
||||||
|
response.value.persons.forEach(person => {
|
||||||
|
if (userPersons.value.map(a => a.relatiecode).includes(person.relatiecode)) {
|
||||||
|
person.checked = true
|
||||||
|
} else {
|
||||||
|
person.checked = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
userAllPersons.value = response.value.persons
|
||||||
|
}
|
||||||
|
|
||||||
|
return { init, auth, isAuthenticated, userData, userPersons, userAllPersons, getAllPersons, registrationToken, getPersons, userLoaded, user }
|
||||||
|
})
|
38
frontend/stores/usersStore.js
Normal file
38
frontend/stores/usersStore.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { getDocs, collection, getFirestore } from 'firebase/firestore'
|
||||||
|
|
||||||
|
export const useUsersStore = defineStore('users', () => {
|
||||||
|
const ledenlijst = ref([])
|
||||||
|
const users = ref([])
|
||||||
|
|
||||||
|
const db = getFirestore()
|
||||||
|
|
||||||
|
const getLedenlijst = async () => {
|
||||||
|
if (ledenlijst[0]) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const querySnapshot = await getDocs(collection(db, "ledenlijst"));
|
||||||
|
querySnapshot.forEach((doc) => {
|
||||||
|
ledenlijst.value.push(doc.data())
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
ledenlijst.value.sort((a, b) => a.fullName.localeCompare(b.fullName))
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUsers = async () => {
|
||||||
|
if (users[0]) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const querySnapshot = await getDocs(collection(db, "users"));
|
||||||
|
querySnapshot.forEach((doc) => {
|
||||||
|
users.value.push(doc.data())
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ledenlijst, users, getLedenlijst, getUsers }
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user