Finalize integration of group bookings into myep spa

This commit is contained in:
Björn Fromme
2020-09-23 11:17:59 +02:00
parent 733fe11f89
commit c25efb717b
14 changed files with 255 additions and 119 deletions
+18
View File
@@ -2551,6 +2551,11 @@
"tweetnacl": "^0.14.3"
}
},
"bezier-easing": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
"integrity": "sha1-wE3+i5JtbsrKGBPWn/F5t8ICXYY="
},
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -10646,6 +10651,11 @@
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
"dev": true
},
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -11129,6 +11139,14 @@
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.3.tgz",
"integrity": "sha512-BADg1mjGWX18Dpmy6bOGzGNnk7B/ZA0RxuA6qedY/YJwirMfKXIDzcccmHbQI0A6k5PzMdMloc0ElHfyOoX35A=="
},
"vue-scrollto": {
"version": "2.18.2",
"resolved": "https://registry.npmjs.org/vue-scrollto/-/vue-scrollto-2.18.2.tgz",
"integrity": "sha512-LQOsuCfDJ5JXsRBb6frM85sCFABZdkwwwchLh4k3wbp8P6URRDcQvwp31U4cTnbyYEOo6JFP7+3p0wkvwy7MdA==",
"requires": {
"bezier-easing": "2.1.0"
}
},
"vue-session": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/vue-session/-/vue-session-1.0.0.tgz",
+2
View File
@@ -29,8 +29,10 @@
"slick-carousel": "^1.8.0",
"sticky-js": "^1.3.0",
"tailwindcss": "^1.8.10",
"tinycolor2": "^1.4.1",
"vue": "^2.6.12",
"vue-router": "^3.4.3",
"vue-scrollto": "^2.18.2",
"vue-session": "^1.0.0",
"vuex": "^3.5.1"
},
@@ -134,7 +134,3 @@ page {
)
headerData.999.stdWrap.if.isTrue = {$plugin.tx_eptheme.settings.googleTagManagerId}
}
[applicationContext == "Development"]
page.includeCSS.main.disableCompression = 1
[END]
@@ -118,15 +118,8 @@ lib.stdheader.3.headerClass >
@import "EXT:ep_theme/Configuration/TypoScript/Page/Export.typoscript"
config {
doctype = html5
removeDefaultCSS = 1
removePageCss = 1
removeDefaultJS = 0
disablePrefixComment = 1
inlineStyle2TempFile = 0
absRefPrefix = /
compressJs = 0
compressCss = 0
compressJs = 1
compressCss = 1
typolinkCheckRootline = 1
typolinkEnableLinksAcrossDomains = 1
sendCacheHeaders = 1
@@ -1,6 +1,6 @@
<template>
<div class="form-wrapper form--border" id="myep-main" v-show="!loading">
<h1>Adressdaten</h1>
<p class="text-2xl">Adressdaten</p>
<form @submit.prevent="save()">
<div class="row">
<div class="col-xs-12 col-md-6">
@@ -1,31 +1,23 @@
<template>
<div id="myep" class="relative">
<nav class="navbar navbar-default" v-if="isLoggedIn">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#myep-navbar" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">My E&amp;P</a>
<div id="myep" class="relative mb-16">
<nav class="w-full bg-gray-200 flex items-center justify-between mb-2 relative" v-if="isLoggedIn">
<span class="block text-2xl px-4">My E&P</span>
<div class="absolute z-10 top-100 left-0 w-full bg-gray-200 shadow lg:relative lg:top-auto lg:left-auto lg:w-auto lg:shadow-none lg:flex lg:space-x-4"
:class="{ 'hidden': !navVisible }">
<div class="lg:flex lg:items-center m-0">
<router-link :to="{ name: 'address' }" class="block p-4 text-black hover:bg-gray-400 hover:text-gray-800" active-class="bg-gray-400 text-gray-800">Adressdaten</router-link>
<router-link :to="{ name: 'newsletter' }" class="block p-4 text-black hover:bg-gray-400 hover:text-gray-800" active-class="bg-gray-400 text-gray-800">Newsletter</router-link>
<router-link :to="{ name: 'profile' }" class="block p-4 text-black hover:bg-gray-400 hover:text-gray-800" active-class="bg-gray-400 text-gray-800">Mein Profil</router-link>
<router-link :to="{ name: 'events' }" class="block p-4 text-black hover:bg-gray-400 hover:text-gray-800" active-class="bg-gray-400 text-gray-800">Buchungen</router-link>
<router-link :to="{ name: 'teamer' }" class="block p-4 text-black hover:bg-gray-400 hover:text-gray-800" active-class="bg-gray-400 text-gray-800" v-if="isTeamer">Teamer</router-link>
</div>
<div class="collapse navbar-collapse" id="myep-navbar">
<ul class="nav navbar-nav">
<router-link :to="{ name: 'address' }" tag="li" active-class="active" :class="{ disabled: loading }"><a>Adressdaten</a></router-link>
<router-link :to="{ name: 'newsletter' }" tag="li" active-class="active" :class="{ disabled: loading }"><a>Newsletter</a></router-link>
<router-link :to="{ name: 'profile' }" tag="li" active-class="active" :class="{ disabled: loading }"><a>Mein Profil</a></router-link>
<router-link :to="{ name: 'events' }" tag="li" active-class="active" :class="{ disabled: loading }"><a>Buchungen</a></router-link>
<router-link :to="{ name: 'teamer' }" tag="li" active-class="active" :class="{ disabled: loading }" v-if="isTeamer"><a>Teamer</a></router-link>
</ul>
<ul class="nav navbar-nav navbar-right">
<li :class="{ disabled: loading }"><a href="#" @click.prevent="logout()">Logout</a></li>
</ul>
<a class="block px-4 py-4 text-black hover:bg-gray-400 hover:text-gray-800" href="#" @click.prevent="logout()">Logout</a>
</div>
<a href="#" class="lg:hidden p-4 transform text-black hover:text-gray-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>
</a>
</nav>
<div class="row" v-if="alert.message">
<div class="col-xs-12">
<div class="py-2" v-if="alert.message">
<div class="alert alert-dismissable" :class="alert.class">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
@@ -33,9 +25,9 @@
{{ alert.message }}
</div>
</div>
</div>
<div class="absolute top-0 left-0 w-full h-full z-100 bg-overlay-white" v-show="loading">
<img :src="config.loaderUrl" alt="" class="absolute top-1/2 left-1/2 transform -translate-xy-1/2">
<div class="flex items-center space-x-2 h-8 mb-2" v-show="loading">
<img :src="config.loaderUrl" alt="" class="block w-4 h-4">
<span v-text="statusMessage"></span>
</div>
<router-view></router-view>
</div>
@@ -57,8 +49,13 @@
}).catch(error => {});
}
},
data () {
return {
navVisible: false
}
},
computed: {
...mapState(['config', 'loading', 'alert']),
...mapState(['config', 'loading', 'alert', 'statusMessage']),
...mapGetters(['isLoggedIn', 'isTeamer'])
},
mounted () {
@@ -31,10 +31,10 @@
</div>
</div>
<div class="py-4 lg:flex lg:justify-between space-y-1 lg:space-y-0" v-show="!loading">
<button class="btn btn-primary" :disabled="!dirty" @click.prevent="storeBookingData()" v-if="initialized">
<button class="button" :disabled="!dirty" @click.prevent="storeBookingData()" v-if="initialized">
Buchung aktualisieren
</button>
<router-link :to="{ name: 'events' }" class="btn btn-primary">
<router-link :to="{ name: 'events' }" class="button">
zur Übersicht
</router-link>
</div>
@@ -117,33 +117,19 @@
</collapsible-panel>
</div>
<div class="py-4 lg:flex lg:justify-between space-y-1 lg:space-y-0" v-show="!loading">
<button class="btn btn-primary" :disabled="!dirty" @click.prevent="storeBookingData()" v-if="initialized">
<button class="button" :disabled="!dirty" @click.prevent="storeBookingData()" v-if="initialized">
Buchung aktualisieren
</button>
<router-link :to="{ name: 'events' }" class="btn btn-primary">
<router-link :to="{ name: 'events' }" class="button">
zur Übersicht
</router-link>
</div>
<div class="py-4 text-center" v-show="statusMessage" v-text="statusMessage" />
<div class="modal fade" tabindex="-1" role="dialog" ref="modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ modal.title }}</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p v-html="modal.message"/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import VueScrollTo from 'vue-scrollto'
import AccommodationSelect from './components/AccommodationSelect'
import ContingentsOverview from './components/ContingentsOverview';
import MutableFields from './components/MutableFields';
@@ -157,9 +143,11 @@
import ServicesOverview from './components/ServicesOverview';
import TransportationSelect from './components/TransportationSelect';
import authenticatedClient from "../api";
import authenticatedClient from '../api';
import {mapMutations, mapState} from 'vuex';
Vue.use(VueScrollTo)
export default {
name: 'BookingEdit',
components: {
@@ -182,7 +170,6 @@
initialized: false,
dirty: false,
processing: false,
statusMessage: '',
bookingData: null,
participantData: null,
invalidParticipantData: {},
@@ -218,16 +205,8 @@
this.endLoading()
});
},
showModal (message, title = 'Hinweis') {
this.modal = {
message: message,
title: title
};
$(this.$refs.modal).modal('show');
},
loadBookingData () {
this.beginLoading()
this.statusMessage = 'Lade Reise- und Buchungsdaten';
this.beginLoading('Lade Reise- und Buchungsdaten...')
return authenticatedClient.get(`/api/booking/${this.bookingId}`)
.then(response => {
this.bookingData = response.data.bookingData;
@@ -238,13 +217,11 @@
throw new Error('Reise- und Buchungsdaten konnten nicht geladen werden.');
})
.finally(() => {
this.statusMessage = '';
this.endLoading()
})
},
loadEventData () {
this.beginLoading()
this.statusMessage = 'Lade Vorgangsdaten';
this.beginLoading('Lade Vorgangsdaten...')
return authenticatedClient.get(`/api/event/${this.bookingData.bookingNumber}`)
.then(response => {
this.eventData = response.data;
@@ -253,13 +230,11 @@
throw new Error(error.response.data.message);
})
.finally(() => {
this.statusMessage = '';
this.endLoading()
})
},
loadMutableFields () {
this.beginLoading()
this.statusMessage = 'Lade mögliche Änderungen';
this.beginLoading('Lade mögliche Änderungen...')
return authenticatedClient.get(`/api/mutable-fields/${this.travelData.busproId}`)
.then(response => {
this.mutableFields = response.data;
@@ -268,13 +243,11 @@
throw new Error('Mögliche Änderungen konnten nicht geladen werden.');
})
.finally(() => {
this.statusMessage = '';
this.endLoading()
})
},
loadContingents () {
this.beginLoading()
this.statusMessage = 'Lade Kontingente';
this.beginLoading('Lade Kontingente...')
return authenticatedClient.get(`/api/contingents/${this.travelData.busproId}`)
.then(response => {
this.contingents = response.data;
@@ -283,13 +256,11 @@
throw new Error('Kontingente konnten nicht geladen werden.');
})
.finally(() => {
this.statusMessage = '';
this.endLoading()
})
},
loadServices () {
this.beginLoading()
this.statusMessage = 'Lade Leistungen';
this.beginLoading('Lade Leistungen...')
return authenticatedClient.get(`/api/services/${this.travelData.busproId}`)
.then(response => {
this.services = response.data;
@@ -298,7 +269,6 @@
throw new Error('Leistungen konnten nicht geladen werden.');
})
.finally(() => {
this.statusMessage = '';
this.endLoading()
})
},
@@ -417,9 +387,13 @@
return messages
},
storeBookingData() {
this.$scrollTo('#myep')
let messages = this.validateInput();
if (false === this.dataValid) {
this.showModal(messages.join('<br>'), 'Daten unvollständig');
this.alert({
message: messages.join('<br>'),
class: 'alert-danger'
});
return;
}
this.beginLoading()
@@ -428,10 +402,16 @@
selectedTransportations: this.selectedTransportations,
remarks: this.bookingData.remarks
}).then(response => {
this.showModal(response.data.message);
this.alert({
message: response.data.message,
class: 'alert-info'
});
this.dirty = false;
}).catch(error => {
this.showModal(error.response.data.message)
this.alert({
message: error.response.data.message,
class: 'alert-danger'
})
}).finally(() => {
this.endLoading()
})
@@ -499,10 +479,12 @@
this.initialized = true;
})
.catch(error => {
this.showModal(error.message, 'Fehler');
this.alert({
message: error.message,
class: 'alert-danger'
});
})
.finally(() => {
this.statusMessage = '';
this.endLoading()
})
}
@@ -23,13 +23,13 @@
<td style="white-space: nowrap">{{ calculateBalance(event) }}</td>
<td>{{ status[event.status] }}</td>
<td class="flex space-x-1">
<a :href="getDownloadUrl(event, 'confirmation')" target="_blank" class="btn btn-primary btn-sm">
<a :href="getDownloadUrl(event, 'confirmation')" target="_blank" class="button button--small">
Bestätigung
</a>
<a :href="getDownloadUrl(event, 'documents')" target="_blank" class="btn btn-primary btn-sm" v-if="event.documents">
<a :href="getDownloadUrl(event, 'documents')" target="_blank" class="button button--small" v-if="event.documents">
Reiseinfos
</a>
<router-link :to="{ name: 'bookingEdit', params: { id: event.id } }" v-if="isGroupManager && event.editable" class="btn btn-primary btn-sm">
<router-link :to="{ name: 'bookingEdit', params: { id: event.id } }" v-if="isGroupManager && event.editable" class="button button--small">
bearbeiten
</router-link>
</td>
@@ -95,5 +95,8 @@ export default new VueRouter({
path: '*',
redirect: { name: 'login' }
}
]
],
scrollBehavior (to, from, savedPosition) {
return { selector: '#myep', offset: { x: 0 , y: 160 } }
}
});
@@ -26,6 +26,7 @@ export default new Vuex.Store({
state: {
config: {},
loading: false,
statusMessage: '',
authToken: null,
refreshToken: null,
userData: defaultUserData,
@@ -77,12 +78,14 @@ export default new Vuex.Store({
alert(state, payload) {
state.alert = payload;
},
beginLoading(state) {
state.loading = true;
state.alert = {message: '', class: ''};
beginLoading(state, statusMessage) {
state.loading = true
state.statusMessage = statusMessage
state.alert = {message: '', class: ''}
},
endLoading(state) {
state.loading = false;
state.loading = false
state.statusMessage = ''
},
setCountries(state, countries) {
state.countries = countries;
@@ -0,0 +1,54 @@
// vendor styles
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
// Core variables and mixins
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/mixins";
// Reset and dependencies
@import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/print";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/glyphicons";
// Core CSS
@import "~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/type";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/code";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/grid";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/tables";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/forms";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/buttons";
// Components
@import "~bootstrap-sass/assets/stylesheets/bootstrap/component-animations";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/dropdowns";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/button-groups";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/input-groups";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/navs";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/navbar";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/breadcrumbs";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/pagination";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/pager";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/labels";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/badges";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/jumbotron";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/thumbnails";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/alerts";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/progress-bars";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/media";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/list-group";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/panels";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-embed";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/wells";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/close";
// Components w/ JavaScript
@import "~bootstrap-sass/assets/stylesheets/bootstrap/modals";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/tooltip";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/popovers";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/carousel";
// Utility classes
//@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities";
@import "bootstrap-overrides/utilities";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
@@ -0,0 +1,55 @@
//
// Utility classes
// --------------------------------------------------
// Floats
// -------------------------
.clearfix {
@include clearfix;
}
.center-block {
@include center-block;
}
.pull-right {
float: right !important;
}
.pull-left {
float: left !important;
}
// Toggling content
// -------------------------
// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1
.hide {
display: none;
}
.show {
display: block;
}
.invisible {
visibility: hidden;
}
.text-hide {
@include text-hide;
}
// Hide from screenreaders and browsers
//
// Credit: HTML5 Boilerplate
.hidden {
display: none;
}
// For Affix plugin
// -------------------------
.affix {
position: fixed;
}
@@ -8,10 +8,7 @@
@import "remix/variables";
@import "remix/colors";
// vendor styles
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
@import "~bootstrap-sass/assets/stylesheets/bootstrap";
@import "bootstrap";
@import "mixins";
@@ -73,12 +70,6 @@
}
@layer utilities {
.top-1\/2 {
top: 50%;
}
.left-1\/2 {
left: 50%;
}
.-translate-xy-1\/2 {
transform: translate(-50%, -50%);
}
+46 -4
View File
@@ -1,26 +1,68 @@
const tinycolor = require('tinycolor2')
module.exports = {
future: {
removeDeprecatedGapUtilities: true,
purgeLayersByDefault: true,
},
experimental: {
applyComplexClasses: true,
},
purge: {
mode: 'layers',
layers: ['components', 'utilities'],
content: [
'./public/typo3conf/ext/ep_theme/Configuration/TypoScript/**/*.typoscript',
'./public/typo3conf/ext/ep_theme/Resources/Private/Layouts/**/*.html',
'./public/typo3conf/ext/ep_theme/Resources/Private/Partials/**/*.html',
'./public/typo3conf/ext/ep_theme/Resources/Private/Templates/**/*.html',
'./public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/**/*.js',
'./public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/**/*.vue',
'./public/typo3conf/ext/ep_theme/Classes/ViewHelpers/**/*.php',
],
},
theme: {
fontFamily: {
'sans': [ 'Lato', 'sans-serif' ],
},
extend: {
colors: {
overlay: {
'white': 'rgba(255, 255, 255, 0.85)'
}
}
'overlay': {
'white': tinycolor('#ffffff').setAlpha(0.85).toRgbString(),
},
'ep': {
'primary': '#009fe3',
'secondary': '#f0bc44',
'primary-light': '#009fff',
'primary-10': tinycolor('#009fe3').setAlpha(0.1).toRgbString(),
'primary-80': tinycolor('#009fe3').setAlpha(0.8).toRgbString(),
},
'sbw': {
'primary': '#f0bc44',
'secondary': '#ee525a',
'primary-10': tinycolor('#f0bc44').setAlpha(0.1).toRgbString(),
'primary-80': tinycolor('#f0bc44').setAlpha(0.8).toRgbString(),
},
'snz': {
'primary': '#82C3C6',
'secondary': '#f9c33d',
'primary-10': tinycolor('#82C3C6').setAlpha(0.1).toRgbString(),
'primary-80': tinycolor('#82C3C6').setAlpha(0.8).toRgbString(),
},
'uch': {
'primary': '#b5e400',
'secondary': '#19a3dc',
'primary-10': tinycolor('#b5e400').setAlpha(0.1).toRgbString(),
'primary-80': tinycolor('#b5e400').setAlpha(0.8).toRgbString(),
},
},
inset: {
'100': '100%',
'1/2': '50%',
},
zIndex: {
'100': 100,
},
},
},
variants: {},