Merge branch 'feature/fetch-api' into develop

This commit is contained in:
Björn Fromme
2022-08-22 15:53:57 +02:00
13 changed files with 564 additions and 442 deletions
-27
View File
@@ -30,7 +30,6 @@
"stimulus-use": "^0.25.4", "stimulus-use": "^0.25.4",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-router": "^3.5.2", "vue-router": "^3.5.2",
"vue-scrollto": "^2.20.0",
"vue-session": "^1.0.0", "vue-session": "^1.0.0",
"vuex": "^3.6.2", "vuex": "^3.6.2",
"whatwg-fetch": "^3.6.2" "whatwg-fetch": "^3.6.2"
@@ -2777,11 +2776,6 @@
"integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
"dev": true "dev": true
}, },
"node_modules/bezier-easing": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
"integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig=="
},
"node_modules/big.js": { "node_modules/big.js": {
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -8206,14 +8200,6 @@
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.4.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.4.tgz",
"integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ==" "integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ=="
}, },
"node_modules/vue-scrollto": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/vue-scrollto/-/vue-scrollto-2.20.0.tgz",
"integrity": "sha512-7i+AGKJTThZnMEkhIPgrZjyAX+fXV7/rGdg+CV283uZZwCxwiMXaBLTmIc5RTA4uwGnT+E6eJle3mXQfM2OD3A==",
"dependencies": {
"bezier-easing": "2.1.0"
}
},
"node_modules/vue-session": { "node_modules/vue-session": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/vue-session/-/vue-session-1.0.0.tgz", "resolved": "https://registry.npmjs.org/vue-session/-/vue-session-1.0.0.tgz",
@@ -10808,11 +10794,6 @@
"integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
"dev": true "dev": true
}, },
"bezier-easing": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
"integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig=="
},
"big.js": { "big.js": {
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -14754,14 +14735,6 @@
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.4.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.4.tgz",
"integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ==" "integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ=="
}, },
"vue-scrollto": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/vue-scrollto/-/vue-scrollto-2.20.0.tgz",
"integrity": "sha512-7i+AGKJTThZnMEkhIPgrZjyAX+fXV7/rGdg+CV283uZZwCxwiMXaBLTmIc5RTA4uwGnT+E6eJle3mXQfM2OD3A==",
"requires": {
"bezier-easing": "2.1.0"
}
},
"vue-session": { "vue-session": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/vue-session/-/vue-session-1.0.0.tgz", "resolved": "https://registry.npmjs.org/vue-session/-/vue-session-1.0.0.tgz",
-1
View File
@@ -33,7 +33,6 @@
"stimulus-use": "^0.25.4", "stimulus-use": "^0.25.4",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-router": "^3.5.2", "vue-router": "^3.5.2",
"vue-scrollto": "^2.20.0",
"vue-session": "^1.0.0", "vue-session": "^1.0.0",
"vuex": "^3.6.2", "vuex": "^3.6.2",
"whatwg-fetch": "^3.6.2" "whatwg-fetch": "^3.6.2"
@@ -62,13 +62,12 @@
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import { mapState, mapMutations } from 'vuex' import { mapState, mapMutations, mapActions } from 'vuex'
import axios from 'axios'
import flatpickr from 'flatpickr' import flatpickr from 'flatpickr'
import { German } from 'flatpickr/dist/l10n/de' import { German } from 'flatpickr/dist/l10n/de'
import FormGroup from './components/FormGroup' import FormGroup from './components/FormGroup'
export default { export default {
components: { components: {
FormGroup FormGroup
}, },
@@ -76,15 +75,18 @@
return { return {
picker: null, picker: null,
formErrors: {}, formErrors: {},
countries: []
} }
}, },
created () { created () {
this.loadFormOptions(); this.loadAddress()
this.$store.dispatch('loadAddress')
.then(() => { .then(() => {
this.picker.setDate(new Date(this.userData.dateOfBirth), true); this.picker.setDate(new Date(this.userData.dateOfBirth), true);
}); this.$emit('loaded')
})
.catch(() => {
this.logout()
this.$router.push({name: 'login'})
})
}, },
mounted () { mounted () {
Vue.nextTick(() => { Vue.nextTick(() => {
@@ -99,37 +101,32 @@
}) })
}, },
methods: { methods: {
...mapMutations(['alert', 'beginLoading', 'endLoading']), ...mapMutations(['logout', 'alert']),
loadFormOptions () { ...mapActions(['loadAddress', 'saveAddress']),
this.beginLoading();
return axios({
url: '/api/countries'
}).then(response => {
this.countries = response.data
}).catch(error => {
}).finally(() => {
this.endLoading()
});
},
save () { save () {
this.formErrors = {}; this.formErrors = {}
this.$store.dispatch('saveAddress', this.userData) this.saveAddress(this.userData)
.then(() => {
this.$emit('loaded')
})
.catch(error => { .catch(error => {
this.alert({ this.alert({
message: 'Bitte überprüfe deine Eingaben.', message: 'Bitte überprüfe deine Eingaben.',
success: false, success: false,
}); })
const violations = error.response.data.violations; error.response.then(data => {
if (typeof violations !== 'undefined') { const violations = data.violations
for (let violation of violations) { if (typeof violations !== 'undefined') {
this.$set(this.formErrors, violation.property_path, violation.message); for (let violation of violations) {
this.$set(this.formErrors, violation.property_path, violation.message)
}
} }
} })
}); })
} }
}, },
computed: { computed: {
...mapState(['loading', 'userData']) ...mapState(['loading', 'userData', 'countries'])
} }
} }
</script> </script>
@@ -1,8 +1,9 @@
<template> <template>
<div id="myep" class="relative mb-16"> <div class="relative mb-16">
<nav class="w-full bg-zinc-50 flex items-center justify-between mb-8 relative" v-if="isLoggedIn"> <a ref="container" class="block w-0 h-0 pt-24 -mt-24"></a>
<span class="block text-2xl px-4">My E&P</span> <nav class="w-full bg-zinc-50 flex items-center mb-8 relative" v-if="isLoggedIn">
<div class="absolute z-50 top-100 left-0 w-full bg-zinc-50 shadow lg:relative lg:top-auto lg:left-auto lg:w-auto lg:shadow-none lg:flex lg:space-x-4" <div class="flex-1 block text-2xl px-4">My E&P</div>
<div class="bg-zinc-50 shadow w-full lg:w-auto absolute lg:relative z-10 lg:z-auto top-full lg:top-auto bg-white left-0 lg:relative lg:shadow-none lg:flex lg:space-x-4"
:class="{ 'hidden': !navVisible }"> :class="{ 'hidden': !navVisible }">
<div class="lg:flex lg:items-center m-0"> <div class="lg:flex lg:items-center m-0">
<router-link @click.native="navVisible = false" :to="{ name: 'address' }" class="block p-4 text-black hover:bg-zinc-400 hover:text-zinc-800" active-class="bg-zinc-400 text-zinc-800">Adressdaten</router-link> <router-link @click.native="navVisible = false" :to="{ name: 'address' }" class="block p-4 text-black hover:bg-zinc-400 hover:text-zinc-800" active-class="bg-zinc-400 text-zinc-800">Adressdaten</router-link>
@@ -11,7 +12,7 @@
<router-link @click.native="navVisible = false" :to="{ name: 'events' }" class="block p-4 text-black hover:bg-zinc-400 hover:text-zinc-800" active-class="bg-zinc-400 text-zinc-800">Buchungen</router-link> <router-link @click.native="navVisible = false" :to="{ name: 'events' }" class="block p-4 text-black hover:bg-zinc-400 hover:text-zinc-800" active-class="bg-zinc-400 text-zinc-800">Buchungen</router-link>
<router-link @click.native="navVisible = false" :to="{ name: 'teamer' }" class="block p-4 text-black hover:bg-zinc-400 hover:text-zinc-800" active-class="bg-zinc-400 text-zinc-800" v-if="isTeamer">Teamer</router-link> <router-link @click.native="navVisible = false" :to="{ name: 'teamer' }" class="block p-4 text-black hover:bg-zinc-400 hover:text-zinc-800" active-class="bg-zinc-400 text-zinc-800" v-if="isTeamer">Teamer</router-link>
</div> </div>
<a class="block px-4 py-4 text-black hover:bg-zinc-400 hover:text-zinc-800" href="#" @click.prevent="logout()">Logout</a> <a class="block px-4 py-4 text-black hover:bg-zinc-400 hover:text-zinc-800" href="#" @click.prevent="performLogout()">Logout</a>
</div> </div>
<a href="#" class="lg:hidden p-4 text-black hover:text-zinc-800" :class="{ 'rotate-90': navVisible }" @click.prevent="navVisible = !navVisible"> <a href="#" class="lg:hidden p-4 text-black hover:text-zinc-800" :class="{ 'rotate-90': navVisible }" @click.prevent="navVisible = !navVisible">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg> <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
@@ -19,7 +20,7 @@
</nav> </nav>
<div class="p-4 mb-8 text-white" :class="{'bg-emerald-600': alert.success, 'bg-red-600': !alert.success}" v-if="alert.message" v-html="alert.message"></div> <div class="p-4 mb-8 text-white" :class="{'bg-emerald-600': alert.success, 'bg-red-600': !alert.success}" v-if="alert.message" v-html="alert.message"></div>
<div class="relative"> <div class="relative">
<router-view></router-view> <router-view @loaded="scrollTop()"></router-view>
<div class="absolute top-0 left-0 inset-0 bg-overlay-white" v-show="loading"> <div class="absolute top-0 left-0 inset-0 bg-overlay-white" v-show="loading">
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 flex items-center space-x-2 h-8"> <div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 flex items-center space-x-2 h-8">
<img :src="config.loaderUrl" alt="" class="block w-4 h-4"> <img :src="config.loaderUrl" alt="" class="block w-4 h-4">
@@ -31,7 +32,7 @@
</template> </template>
<script> <script>
import { mapGetters, mapState } from 'vuex' import { mapGetters, mapState, mapActions, mapMutations } from 'vuex'
import store from './store' import store from './store'
import router from './router' import router from './router'
@@ -39,12 +40,23 @@
store, store,
router, router,
methods: { methods: {
logout () { performLogout () {
this.$store.dispatch('logout') this.logout()
.then(() => { .then(() => {
this.$router.replace({ name: 'login' }) this.$router.replace({ name: 'login' })
}).catch(error => {}); }).catch(error => {});
} },
scrollTop() {
this.$refs.container.scrollIntoView({behavior: 'smooth', block: 'start'})
},
...mapMutations([
'initConfig',
]),
...mapActions([
'checkLoginState',
'loadCountries',
'logout',
]),
}, },
data () { data () {
return { return {
@@ -56,11 +68,16 @@
...mapGetters(['isLoggedIn', 'isTeamer']) ...mapGetters(['isLoggedIn', 'isTeamer'])
}, },
mounted () { mounted () {
this.$store.dispatch('init').then(() => { this.initConfig()
if (this.isLoggedIn) { this.checkLoginState()
this.$router.replace({ name: 'address' }) .then(() => {
} if (this.isLoggedIn) {
}) this.loadCountries()
.then(() => {
this.$router.replace({ name: 'address' })
})
}
})
}, },
} }
</script> </script>
@@ -158,7 +158,6 @@
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import VueScrollTo from 'vue-scrollto'
import AccommodationSelect from './components/AccommodationSelect' import AccommodationSelect from './components/AccommodationSelect'
import ContingentsOverview from './components/ContingentsOverview' import ContingentsOverview from './components/ContingentsOverview'
import MutableFields from './components/MutableFields' import MutableFields from './components/MutableFields'
@@ -171,10 +170,8 @@
import ServiceSelect from './components/ServiceSelect' import ServiceSelect from './components/ServiceSelect'
import ServicesOverview from './components/ServicesOverview' import ServicesOverview from './components/ServicesOverview'
import TransportationSelect from './components/TransportationSelect' import TransportationSelect from './components/TransportationSelect'
import axios from 'axios' import { mapGetters, mapMutations, mapState } from 'vuex'
import {mapGetters, mapMutations, mapState} from 'vuex' import { checkStatus, defaultOptions } from './api'
Vue.use(VueScrollTo)
export default { export default {
name: 'BookingEdit', name: 'BookingEdit',
@@ -222,78 +219,95 @@
}, },
methods: { methods: {
...mapMutations(['alert', 'beginLoading', 'endLoading']), ...mapMutations(['alert', 'beginLoading', 'endLoading']),
loadFormOptions () {
this.beginLoading();
return axios({
url: '/api/countries',
}).then(response => {
this.countries = response.data
}).catch(error => {
}).finally(() => {
this.endLoading()
});
},
loadBookingData () { loadBookingData () {
this.beginLoading('Lade Reise- und Buchungsdaten...') this.beginLoading('Lade Reise- und Buchungsdaten...')
return axios({ const options = {
url: `/api/booking/${this.bookingId}` ...defaultOptions
}).then(response => { }
this.bookingData = response.data.bookingData; return fetch(`${this.config.myEpApiBaseUrl}/api/booking/${this.bookingId}`, options)
this.participantData = response.data.participantData; .then(checkStatus)
this.travelData = response.data.travelData; .then(response => response.json())
}).catch(() => { .then(data => {
throw new Error('Reise- und Buchungsdaten konnten nicht geladen werden.'); this.bookingData = data.bookingData
}).finally(() => { this.participantData = data.participantData
this.endLoading() this.travelData = data.travelData
}) })
.catch(() => {})
.finally(() => {
this.endLoading()
})
}, },
loadEventData () { loadEventData () {
this.beginLoading('Lade Vorgangsdaten...') this.beginLoading('Lade Vorgangsdaten...')
return axios({ const options = {
url: `/api/event/${this.bookingData.bookingNumber}` ...defaultOptions
}).then(response => { }
this.eventData = response.data; return fetch(`${this.config.myEpApiBaseUrl}/api/event/${this.bookingData.bookingNumber}`, options)
}).catch(error => { .then(checkStatus)
throw new Error(error.response.data.message); .then(response => response.json())
}).finally(() => { .then(data => {
this.endLoading() this.eventData = data
}) })
.catch(error => {
throw new Error(error.message)
})
.finally(() => {
this.endLoading()
})
}, },
loadMutableFields () { loadMutableFields () {
this.beginLoading('Lade mögliche Änderungen...') this.beginLoading('Lade mögliche Änderungen...')
return axios({ const options = {
url: `/api/mutable-fields/${this.travelData.busproId}` ...defaultOptions
}).then(response => { }
this.mutableFields = response.data; return fetch(`${this.config.myEpApiBaseUrl}/api/mutable-fields/${this.travelData.busproId}`, options)
}).catch(() => { .then(checkStatus)
throw new Error('Mögliche Änderungen konnten nicht geladen werden.'); .then(response => response.json())
}).finally(() => { .then(data => {
this.endLoading() this.mutableFields = data
}) })
.catch(() => {
throw new Error('Mögliche Änderungen konnten nicht geladen werden.');
})
.finally(() => {
this.endLoading()
})
}, },
loadContingents () { loadContingents () {
this.beginLoading('Lade Kontingente...') this.beginLoading('Lade Kontingente...')
return axios({ const options = {
url: `/api/contingents/${this.travelData.busproId}` ...defaultOptions
}).then(response => { }
this.contingents = response.data; return fetch(`${this.config.myEpApiBaseUrl}/api/contingents/${this.travelData.busproId}`, options)
}).catch(() => { .then(checkStatus)
throw new Error('Kontingente konnten nicht geladen werden.'); .then(response => response.json())
}).finally(() => { .then(data => {
this.endLoading() this.contingents = data
}) })
.catch(() => {
throw new Error('Kontingente konnten nicht geladen werden.');
})
.finally(() => {
this.endLoading()
})
}, },
loadServices () { loadServices () {
this.beginLoading('Lade Leistungen...') this.beginLoading('Lade Leistungen...')
return axios({ const options = {
url: `/api/services/${this.travelData.busproId}` ...defaultOptions
}).then(response => { }
this.services = response.data; return fetch(`${this.config.myEpApiBaseUrl}/api/services/${this.travelData.busproId}`, options)
}).catch(() => { .then(checkStatus)
throw new Error('Leistungen konnten nicht geladen werden.'); .then(response => response.json())
}).finally(() => { .then(data => {
this.endLoading() this.services = data
}) })
.catch(() => {
throw new Error('Leistungen konnten nicht geladen werden.');
})
.finally(() => {
this.endLoading()
})
}, },
getAccommodationLabel (participant) { getAccommodationLabel (participant) {
if (participant.hasOwnProperty('accommodationId') && participant.accommodationId in this.contingents) { if (participant.hasOwnProperty('accommodationId') && participant.accommodationId in this.contingents) {
@@ -413,7 +427,6 @@
return messages return messages
}, },
storeBookingData() { storeBookingData() {
this.$scrollTo('#myep')
let messages = this.validateInput(); let messages = this.validateInput();
if (false === this.dataValid) { if (false === this.dataValid) {
this.alert({ this.alert({
@@ -423,32 +436,41 @@
return; return;
} }
this.beginLoading() this.beginLoading()
axios({ const options = {
url: `/api/booking/${this.bookingId}`, ...defaultOptions,
method: 'put', method: 'PUT',
data: { body: JSON.stringify({
participantData: this.participantData, participantData: this.participantData,
selectedTransportations: this.selectedTransportations, selectedTransportations: this.selectedTransportations,
remarks: this.bookingData.remarks, remarks: this.bookingData.remarks,
},
}).then(response => {
this.alert({
message: response.data.message,
success: true,
});
this.dirty = false;
}).catch(error => {
this.alert({
message: error.response.data.message,
success: false,
}) })
}).finally(() => { }
this.endLoading() fetch(`${this.config.myEpApiBaseUrl}/api/booking/${this.bookingId}`, options)
}) .then(checkStatus)
.then(response => response.json())
.then(data => {
this.alert({
message: data.message,
success: true,
})
this.dirty = false
})
.catch(error => {
error.response.then(data => {
this.alert({
message: data.message,
success: false,
})
})
})
.finally(() => {
this.endLoading()
this.$emit('loaded')
})
} }
}, },
computed: { computed: {
...mapState(['loading']), ...mapState(['config', 'loading']),
...mapGetters(['isGroupManager']), ...mapGetters(['isGroupManager']),
availableContingents () { availableContingents () {
let filteredContingents = {}; let filteredContingents = {};
@@ -500,14 +522,14 @@
}, },
mounted () { mounted () {
this.bookingId = this.$route.params.id this.bookingId = this.$route.params.id
this.loadFormOptions() this.loadBookingData()
.then(() => this.loadBookingData())
.then(() => this.loadEventData()) .then(() => this.loadEventData())
.then(() => this.loadMutableFields()) .then(() => this.loadMutableFields())
.then(() => this.loadContingents()) .then(() => this.loadContingents())
.then(() => this.loadServices()) .then(() => this.loadServices())
.then(() => { .then(() => {
this.initialized = true; this.initialized = true;
this.$emit('loaded')
}) })
.catch(error => { .catch(error => {
this.alert({ this.alert({
@@ -515,9 +537,6 @@
success: false, success: false,
}); });
}) })
.finally(() => {
this.endLoading()
})
} }
} }
</script> </script>
@@ -51,7 +51,7 @@
<script> <script>
import { mapState, mapMutations } from 'vuex' import { mapState, mapMutations } from 'vuex'
import axios from 'axios' import { defaultOptions, checkStatus } from './api'
export default { export default {
data () { data () {
@@ -62,43 +62,59 @@
} }
}, },
created () { created () {
this.beginLoading(); this.beginLoading()
return axios({ const options = {
url: '/api/crm-selections' ...defaultOptions
}).then(response => { }
this.crmData = response.data
}).catch(error => { return fetch(`${this.config.myEpApiBaseUrl}/api/crm-selections`, options)
this.alert({ .then(checkStatus)
message: 'Es ist ein Fehler aufgetreten :(', .then(response => response.json())
success: false, .then(data => {
this.crmData = data
})
.catch(() => {
this.alert({
message: 'Es ist ein Fehler aufgetreten :(',
success: false,
})
})
.finally(() => {
this.endLoading()
this.$emit('loaded')
}) })
}).finally(() => {
this.endLoading()
});
}, },
methods: { methods: {
...mapMutations(['beginLoading', 'endLoading', 'alert']), ...mapMutations(['beginLoading', 'endLoading', 'alert']),
save () { save () {
this.beginLoading(); this.beginLoading()
return axios({ const options = {
url: '/api/crm-selections', ...defaultOptions,
method: 'put', method: 'PUT',
data: this.crmData body: JSON.stringify(this.crmData)
}).then(() => { }
this.alert({
message: 'Die Änderungen wurden gespeichert', return fetch(`${this.config.myEpApiBaseUrl}/api/crm-selections`, options)
success: true, .then(checkStatus)
}); .then(response => response.json())
}).catch(error => { .then(() => {
this.alert({ this.alert({
message: 'Es ist ein Fehler aufgetreten :(', message: 'Die Änderungen wurden gespeichert',
success: false, success: true,
}); })
}).finally(() => { })
this.endLoading(); .catch(() => {
}); this.alert({
message: 'Es ist ein Fehler aufgetreten :(',
success: false,
})
})
.finally(() => {
this.endLoading()
this.$emit('loaded')
})
}, },
hasVisibleSelections (group) { hasVisibleSelections (group) {
if (this.hiddenSelectionGroups.indexOf(group.id) !== -1) { if (this.hiddenSelectionGroups.indexOf(group.id) !== -1) {
@@ -116,7 +132,7 @@
} }
}, },
computed: { computed: {
...mapState(['loading']) ...mapState(['config', 'loading'])
} }
} }
</script> </script>
@@ -75,10 +75,10 @@
</template> </template>
<script> <script>
import {mapState, mapMutations, mapGetters} from 'vuex' import { mapState, mapMutations, mapGetters } from 'vuex'
import axios from 'axios'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import 'dayjs/locale/de' import 'dayjs/locale/de'
import { checkStatus, defaultOptions } from './api'
export default { export default {
data () { data () {
@@ -96,18 +96,26 @@ import {mapState, mapMutations, mapGetters} from 'vuex'
created () { created () {
this.beginLoading(); this.beginLoading();
return axios({ const options = {
url: '/api/events' ...defaultOptions
}).then(response => { }
this.events = response.data;
}).catch(error => { return fetch(`${this.config.myEpApiBaseUrl}/api/events`, options)
this.alert({ .then(checkStatus)
message: 'Es ist ein Fehler aufgetreten :(', .then(response => response.json())
success: false, .then(data => {
}); this.events = data
}).finally(() => { })
this.endLoading(); .catch(() => {
}); this.alert({
message: 'Es ist ein Fehler aufgetreten :(',
success: false,
})
})
.finally(() => {
this.endLoading()
this.$emit('loaded')
})
}, },
methods: { methods: {
...mapMutations(['beginLoading', 'endLoading', 'alert']), ...mapMutations(['beginLoading', 'endLoading', 'alert']),
@@ -118,11 +126,7 @@ import {mapState, mapMutations, mapGetters} from 'vuex'
return dayjs(date).format('DD.MM.YY') return dayjs(date).format('DD.MM.YY')
}, },
getDownloadUrl (event, type) { getDownloadUrl (event, type) {
return this.config.myEpApiBaseUrl return `${this.config.myEpApiBaseUrl}/api/events/${event.id}/${type}`
+ '/api/events/'
+ event.id
+ '/' + type + '?token='
+ sessionStorage.getItem('myep-auth-token');
}, },
calculateBalance (event) { calculateBalance (event) {
if (!event.price) { if (!event.price) {
@@ -51,7 +51,7 @@
</template> </template>
<script> <script>
import { mapState, mapGetters, mapMutations } from 'vuex' import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
import FormGroup from './components/FormGroup' import FormGroup from './components/FormGroup'
export default { export default {
@@ -66,6 +66,7 @@
}, },
methods: { methods: {
...mapMutations(['alert']), ...mapMutations(['alert']),
...mapActions(['login', 'resetPassword', 'loadCountries']),
toggleMode () { toggleMode () {
this.mode = this.mode === 'login' ? 'reset' : 'login'; this.mode = this.mode === 'login' ? 'reset' : 'login';
this.password = '' this.password = ''
@@ -73,12 +74,12 @@
submit () { submit () {
this.message = ''; this.message = '';
if (this.mode === 'login') { if (this.mode === 'login') {
this.login() this.performLogin()
} else { } else {
this.resetPassword() this.performPasswordReset()
} }
}, },
login () { performLogin () {
this.formErrors = {}; this.formErrors = {};
if (!this.email || !this.password) { if (!this.email || !this.password) {
this.alert({ this.alert({
@@ -87,15 +88,21 @@
}); });
return return
} }
this.$store.dispatch('login', { this.login({
email: this.email, email: this.email,
password: this.password password: this.password
}).then(() => { }).then(() => {
this.$router.push({ name: 'address' }); this.loadCountries()
.then(() => {
this.$router.push({ name: 'address' })
})
}).catch(error => { }).catch(error => {
}); }).finally(() => {
this.email = ''
this.password = ''
})
}, },
resetPassword () { performPasswordReset () {
this.formErrors = {}; this.formErrors = {};
if (!this.email) { if (!this.email) {
this.alert({ this.alert({
@@ -104,9 +111,8 @@
}); });
return; return;
} }
this.$store.dispatch('resetPassword', this.resetPassword(this.email)
this.email .then(response => {
).then(response => {
this.email = ''; this.email = '';
if (response.data.success) { if (response.data.success) {
this.alert({ this.alert({
@@ -130,7 +136,9 @@
this.$set(this.formErrors, violation.property_path, violation.message); this.$set(this.formErrors, violation.property_path, violation.message);
} }
} }
}); }).finally(() => {
this.$emit('loaded')
})
} }
}, },
computed: { computed: {
@@ -12,7 +12,7 @@
<script> <script>
import { mapState, mapMutations } from 'vuex' import { mapState, mapMutations } from 'vuex'
import axios from 'axios' import { checkStatus, defaultOptions } from './api'
export default { export default {
methods: { methods: {
@@ -28,33 +28,44 @@
this.beginLoading(); this.beginLoading();
return axios({ const options = {
url: '/api/newsletter', ...defaultOptions,
method: 'put', method: 'PUT',
data body: JSON.stringify(data)
}).then(() => { }
this.alert({
message: 'Die Änderungen wurden gespeichert', return fetch(`${this.config.myEpApiBaseUrl}/api/newsletter`, options)
success: true, .then(checkStatus)
}); .then(response => response.json())
}).catch(error => { .then(() => {
this.alert({ this.alert({
message: 'Es ist ein Fehler aufgetreten :(', message: 'Die Änderungen wurden gespeichert',
success: false, success: true,
}); })
}).finally(() => { })
this.endLoading(); .catch(() => {
}); this.alert({
message: 'Es ist ein Fehler aufgetreten :(',
success: false,
})
})
.finally(() => {
this.endLoading()
this.$emit('loaded')
})
} }
}, },
computed: { computed: {
...mapState(['loading', 'userData']), ...mapState(['config', 'loading', 'userData']),
statusMessage () { statusMessage () {
let message = 'Du bist aktuell'; let message = 'Du bist aktuell';
message += this.userData.newsletter ? ' ' : ' <strong>nicht</strong> '; message += this.userData.newsletter ? ' ' : ' <strong>nicht</strong> ';
message += 'zum Newsletter angemeldet.'; message += 'zum Newsletter angemeldet.';
return message; return message;
} }
},
created() {
this.$emit('loaded')
} }
} }
</script> </script>
@@ -1,81 +1,77 @@
<template> <template>
<div> <div>
<h1>Teamer</h1> <p class="text-2xl pb-4">Teamer</p>
<form @submit.prevent="save()"> <form @submit.prevent="save()">
<div class="row"> <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="col-xs-12 col-md-6"> <div>
<h4>Fährst du Ski oder Board?</h4> <h4 class="mb-2">Fährst du Ski oder Board?</h4>
<div class="form-group"> <div class="flex flex-col space-y-1 pb-4">
<div class="checkbox" v-for="ability of abilities"> <label class="flex items-center"
<label> v-for="ability of abilities" :key="ability.id">
<input type="checkbox" class="form-control" v-model="userData.abilities" <input type="checkbox"
:value="ability.id" /> class="border-zinc-400 focus:border-zinc-800 ring-0 focus:ring-0 text-ep-secondary sbw:text-sbw-secondary"
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span> v-model="userData.abilities"
{{ ability['title'] }} :value="ability.id"/>
</label> <span class="ml-2">{{ ability.title }}</span>
</div> </label>
</div> </div>
<h4>Mögliche Jobprofile</h4> <h4 class="mb-2">Mögliche Jobprofile</h4>
<div class="form-group"> <div class="flex flex-col space-y-1 pb-4">
<div class="checkbox" v-for="jobProfile of jobProfiles"> <label class="flex items-center"
<label> v-for="jobProfile of jobProfiles" :key="jobProfile.id">
<input type="checkbox" class="form-control" v-model="userData.jobProfiles" <input type="checkbox"
:value="jobProfile.id" /> class="border-zinc-400 focus:border-zinc-800 ring-0 focus:ring-0 text-ep-secondary sbw:text-sbw-secondary"
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span> v-model="userData.jobProfiles"
{{ jobProfile['title'] }} :value="jobProfile.id"/>
</label> <span class="ml-2">{{ jobProfile.title }}</span>
</div> </label>
</div> </div>
<h4>Sonstiges</h4> <h4 class="mb-2">Sonstiges</h4>
<div class="form-group"> <div class="flex flex-col space-y-1 pb-4">
<div class="checkbox"> <label class="flex items-center">
<label> <input type="checkbox"
<input type="checkbox" class="form-control" v-model="userData.wantsToLead"/> class="border-zinc-400 focus:border-zinc-800 ring-0 focus:ring-0 text-ep-secondary sbw:text-sbw-secondary"
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span> v-model="userData.wantsToLead"/>
Ich möchte eine Fahrtenleitung übernehmen. <span class="ml-2">Ich möchte eine Fahrtenleitung übernehmen</span>
</label> </label>
</div>
</div> </div>
<form-group label="Status"> <form-group label="Status">
<select id="status" v-model="userData.seniority" class="form-control"> <select id="status" v-model="userData.seniority" class="border-zinc-400 focus:border-zinc-800 ring-0 focus:ring-0 mt-1 w-full">
<option v-for="status in this.status" :value="status.value">{{ status.label }}</option> <option v-for="status in this.status" :value="status.value">{{ status.label }}</option>
</select> </select>
</form-group> </form-group>
<form-group label="Jackengröße"> <form-group label="Jackengröße">
<select v-model="userData.jacketSize" class="form-control"> <select v-model="userData.jacketSize" class="border-zinc-400 focus:border-zinc-800 ring-0 focus:ring-0 mt-1 w-full">
<option v-for="size of this.sizes" :value="size">{{ size }}</option> <option v-for="size of this.sizes" :value="size">{{ size }}</option>
</select> </select>
</form-group> </form-group>
<form-group label="Wünsche"> <form-group label="Wünsche">
<textarea class="xxlarge form-control" cols="30" rows="3" v-model="userData.notes"></textarea> <textarea class="w-full" cols="30" rows="3" v-model="userData.notes"></textarea>
</form-group> </form-group>
</div> </div>
<div class="col-xs-12 col-md-6"> <div>
<h4>Verfügbarkeiten</h4> <h4 class="mb-2">Verfügbarkeiten</h4>
<div class="checkbox" v-for="timeFrame in timeFrames"> <div class="flex flex-col space-y-1 pb-4">
<label> <label class="flex items-center"
<input type="checkbox" class="form-control" v-model="userData.availableTimeFrames" v-for="timeFrame of timeFrames" :key="timeFrame.id">
<input type="checkbox"
class="border-zinc-400 focus:border-zinc-800 ring-0 focus:ring-0 text-ep-secondary sbw:text-sbw-secondary"
v-model="userData.availableTimeFrames"
:value="timeFrame.id"/> :value="timeFrame.id"/>
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span> <span class="ml-2">{{ timeFrame | period }}</span>
{{ timeFrame | period }}
</label> </label>
</div> <label class="flex items-center">
<div class="checkbox"> <input type="checkbox"
<label> class="border-zinc-400 focus:border-zinc-800 ring-0 focus:ring-0 text-ep-secondary sbw:text-sbw-secondary"
<input type="checkbox" class="form-control" v-model="userData.monthLong"/> v-model="userData.monthLong"/>
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span> <span class="ml-2">Ich habe Zeit und würde gerne über einen Monat am Stück in eine Destination.</span>
Ich habe Zeit und würde gerne über einen Monat am Stück in eine Destination.
</label> </label>
</div> </div>
</div> </div>
</div> <div class="col-span-1 md:col-span2">
<div class="row"> <button type="submit" class="button bg-button" :disabled="loading">
<div class="col-xs-12"> Speichern
<div class="form-group"> </button>
<button type="submit" class="button bg-button" :disabled="loading">
Speichern
</button>
</div>
</div> </div>
</div> </div>
</form> </form>
@@ -84,10 +80,10 @@
<script> <script>
import { mapState, mapMutations } from 'vuex' import { mapState, mapMutations } from 'vuex'
import axios from 'axios'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import 'dayjs/locale/de' import 'dayjs/locale/de'
import FormGroup from './components/FormGroup' import FormGroup from './components/FormGroup'
import { checkStatus, defaultOptions } from './api'
export default { export default {
components: { components: {
@@ -121,42 +117,57 @@
this.beginLoading(); this.beginLoading();
return axios({
url: '/api/teamer-data', const options = {
method: 'put', ...defaultOptions,
data method: 'PUT',
}) body: JSON.stringify(data)
.then(() => { }
this.alert({
message: 'Die Änderungen wurden gespeichert', return fetch(`${this.config.myEpApiBaseUrl}/api/teamer-data`, options)
success: true, .then(checkStatus)
}); .then(response => response.json())
}).catch(error => { .then(() => {
this.alert({ this.alert({
message: 'Es ist ein Fehler aufgetreten :(', message: 'Die Änderungen wurden gespeichert',
success: false, success: true,
}); })
}).finally(() => { })
this.endLoading(); .catch(() => {
}); this.alert({
message: 'Es ist ein Fehler aufgetreten :(',
success: false,
})
})
.finally(() => {
this.endLoading()
this.$emit('loaded')
})
} }
}, },
created () { created () {
this.beginLoading(); this.beginLoading();
return axios({ const options = {
url: '/api/teamer-data' ...defaultOptions
}).then(response => { }
this.abilities = response.data.abilities;
this.timeFrames = response.data.timeFrames; return fetch(`${this.config.myEpApiBaseUrl}/api/teamer-data`, options)
this.jobProfiles = response.data.jobProfiles; .then(checkStatus)
}).catch(error => { .then(response => response.json())
}).finally(() => { .then(data => {
this.endLoading(); this.abilities = data.abilities
}); this.timeFrames = data.timeFrames
this.jobProfiles = data.jobProfiles
})
.catch(() => {})
.finally(() => {
this.endLoading()
this.$emit('loaded')
})
}, },
computed: { computed: {
...mapState(['userData', 'loading']) ...mapState(['config', 'userData', 'loading'])
}, },
filters: { filters: {
period (timeFrame) { period (timeFrame) {
@@ -0,0 +1,26 @@
const defaultOptions = {
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
}
function checkStatus(response) {
if (response.ok) {
return response
} else {
const error = new Error(response.status)
error.response = response.json()
throw error
}
}
function handleError() {
commit('alert', {
message: 'Es ist ein Fehler aufgetreten :(',
success: false,
})
throw error
}
export { checkStatus, handleError, defaultOptions }
@@ -4,7 +4,7 @@
{{ label }}: {{ label }}:
</label> </label>
<slot></slot> <slot></slot>
<span class="block text-sm mt-1" v-show="error"> <span class="block text-sm mt-1 text-red-500" v-show="error">
{{ error }} {{ error }}
</span> </span>
</div> </div>
@@ -1,13 +1,10 @@
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import axios from 'axios' import { checkStatus, handleError, defaultOptions } from '../api'
const configElement = document.getElementById('appconfig') const configElement = document.getElementById('appconfig')
const config = JSON.parse(configElement.innerHTML) const config = JSON.parse(configElement.innerHTML)
axios.defaults.baseURL = config.myEpApiBaseUrl
axios.defaults.withCredentials = true
Vue.use(Vuex) Vue.use(Vuex)
const defaultUserData = { const defaultUserData = {
@@ -33,6 +30,7 @@ export default new Vuex.Store({
statusMessage: '', statusMessage: '',
authenticated: false, authenticated: false,
userData: defaultUserData, userData: defaultUserData,
countries: [],
alert: { alert: {
message: '', message: '',
success: true, success: true,
@@ -72,125 +70,168 @@ export default new Vuex.Store({
} }
}, },
actions: { actions: {
init({commit}) { checkLoginState({commit, state}) {
commit('initConfig'); commit('beginLoading')
commit('beginLoading');
return axios({ const options = {
url: '/api/ping', ...defaultOptions
method: 'get', }
}).then(response => {
commit('login') return fetch(`${state.config.myEpApiBaseUrl}/api/ping`, options)
}).catch(() => { .then(checkStatus)
}).finally(() => { .then(response => response.json())
commit('endLoading'); .then(() => {
}) commit('login')
})
.catch(() => {})
.finally(() => {
commit('endLoading')
})
},
loadCountries({commit, state}) {
commit('beginLoading')
const options = {
...defaultOptions
}
return fetch(`${state.config.myEpApiBaseUrl}/api/countries`, options)
.then(checkStatus)
.then(response => response.json())
.then(data => {
commit('setCountries', data)
})
.catch(handleError)
.finally(() => {
commit('endLoading')
})
}, },
login({commit}, credentials) { login({commit}, credentials) {
commit('beginLoading'); commit('beginLoading')
return axios({ const options = {
url: '/api/login', ...defaultOptions,
method: 'post', method: 'POST',
data: { body: JSON.stringify({
username: credentials.email, username: credentials.email,
password: credentials.password password: credentials.password
}, })
}).then(response => { }
commit('login', response.data);
}).catch(error => {
commit('logout');
commit('alert', {
message: 'Der Login ist fehlgeschlagen :(',
success: false,
});
throw error;
}).finally(() => {
commit('endLoading');
});
},
logout({commit}) {
commit('beginLoading');
return axios({ return fetch(`${config.myEpApiBaseUrl}/api/login`, options)
url: '/api/logout', .then(checkStatus)
}).catch(error => { .then(response => response.json())
}).finally(() => { .then(data => {
commit('logout'); commit('login', data)
commit('endLoading'); })
}); .catch(error => {
}, commit('logout')
resetPassword({commit}, email) {
commit('beginLoading');
return axios({
url: '/api/reset-password',
data: {email: email},
method: 'post'
}).then(response => {
return response;
}).finally(() => {
commit('endLoading');
});
},
register({commit}, userData) {
commit('beginLoading');
return axios({
url: '/api/register',
data: userData,
method: 'post'
}).then(response => {
return response;
}).finally(() => {
commit('endLoading');
});
},
loadAddress({commit}) {
commit('beginLoading');
return axios({
url: '/api/address',
method: 'get',
}).then(response => {
commit('setUserData', response.data);
if (response.data.watchlist) {
commit('setWatchlist', response.data.watchlist);
}
}).catch(() => {
commit('alert', {
message: 'Es ist ein Fehler aufgetreten :(',
success: false,
});
}).finally(() => {
commit('endLoading');
});
},
saveAddress({commit, state}, data) {
commit('beginLoading');
return axios({
url: '/api/address',
method: 'put',
data
}).then(() => {
commit('alert', {
message: 'Die Änderungen wurden gespeichert',
success: true,
});
})
.catch(error => {
if (error.config.status >= 500) {
commit('alert', { commit('alert', {
message: 'Es ist ein Fehler aufgetreten :(', message: 'Der Login ist fehlgeschlagen :(',
success: false, success: false,
}); });
} else { throw error
throw error; })
} .finally(() => {
}).finally(() => { commit('endLoading')
commit('endLoading'); })
}); },
logout({commit}) {
commit('beginLoading')
const options = {
...defaultOptions
}
return fetch(`${config.myEpApiBaseUrl}/api/logout`, options)
.then(checkStatus)
.catch(() => {})
.finally(() => {
commit('logout')
commit('endLoading')
})
},
resetPassword({commit}, email) {
commit('beginLoading')
const options = {
...defaultOptions,
method: 'POST',
body: JSON.stringify({
email
})
}
return fetch(`${config.myEpApiBaseUrl}/api/reset-password`, options)
.then(checkStatus)
.then(response => response.json())
.then(response => response)
.catch(handleError)
.finally(() => {
commit('endLoading')
})
},
register({commit}, userData) {
commit('beginLoading')
const options = {
...defaultOptions,
method: 'POST',
body: JSON.stringify(userData)
}
return fetch(`${config.myEpApiBaseUrl}/api/register`, options)
.then(checkStatus)
.then(response => response.json())
.then(data => data)
.catch(handleError)
.finally(() => {
commit('endLoading')
})
},
loadAddress({commit}) {
commit('beginLoading')
const options = {
...defaultOptions,
}
return fetch(`${config.myEpApiBaseUrl}/api/address`, options)
.then(checkStatus)
.then(response => response.json())
.then(data => {
commit('setUserData', data)
if (data.watchlist) {
commit('setWatchlist', data.watchlist)
}
})
.catch(handleError)
.finally(() => {
commit('endLoading')
})
},
saveAddress({commit, state}, data) {
commit('beginLoading')
const options = {
...defaultOptions,
method: 'PUT',
body: JSON.stringify(data)
}
return fetch(`${config.myEpApiBaseUrl}/api/address`, options)
.then(checkStatus)
.then(response => response.json())
.then(() => {
commit('alert', {
message: 'Die Änderungen wurden gespeichert',
success: true,
})
})
.catch(handleError)
.finally(() => {
commit('endLoading')
})
} }
}, },
getters: { getters: {