run eslint to fix errors

This commit is contained in:
2023-10-01 15:45:02 +02:00
parent 3850544ebb
commit 5887913f07
59 changed files with 5472 additions and 3235 deletions

View File

@@ -0,0 +1,73 @@
/*
* libreevent - .eslintrc.js
*
* Created by Janis Hutz 02/26/2023, Licensed under the GPL V3 License
* https://janishutz.com, development@janishutz.com
*
*
*/
module.exports = {
'env': {
'browser': true,
'commonjs': true,
'es2021': true,
'node': true
},
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended'
],
'overrides': [
],
'parserOptions': {
'ecmaVersion': 'latest'
},
'rules': {
'indent': [
'error',
4
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
],
'spaced-comment': [
'error',
'always'
],
'arrow-spacing': [
'error',
{ 'before': true, 'after': true }
],
'func-call-spacing': [
'error',
'never'
],
'keyword-spacing': [
'error',
{ 'before': true, 'after': true }
],
'key-spacing': [
'error',
{ 'mode': 'strict' }
],
'space-before-blocks': [
'error',
'always'
],
'space-in-parens': [
'error',
'always'
],
'no-var': 'error'
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,8 @@
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"preview": "vite preview --host", "preview": "vite preview --host",
"build": "vite build" "build": "vite build",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
}, },
"dependencies": { "dependencies": {
"@pdfme/generator": "^1.2.3", "@pdfme/generator": "^1.2.3",
@@ -18,6 +19,8 @@
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^1.10.2", "@vitejs/plugin-vue": "^1.10.2",
"vite": "^2.5.4" "vite": "^2.5.4",
"eslint": "^8.39.0",
"eslint-plugin-vue": "^9.17.0"
} }
} }

File diff suppressed because one or more lines are too long

View File

@@ -169,7 +169,7 @@ export default {
data () { data () {
return { return {
theme: '', theme: '',
} };
}, },
methods: { methods: {
changeTheme () { changeTheme () {
@@ -211,5 +211,5 @@ solution. Your help is greatly appreciated by the team as well as all its users!
=> https://libreevent.janishutz.com/ => https://libreevent.janishutz.com/
` ); ` );
} }
} };
</script> </script>

View File

@@ -45,11 +45,11 @@ export default {
data () { data () {
return { return {
tickets: { 'ticket1': 20, 'ticket2': 20 }, tickets: { 'ticket1': 20, 'ticket2': 20 },
event: { 'name': 'TestEvent2', 'location': 'TestLocation2', 'eventID': 'test2', 'date': '2023-07-15', 'currency': 'CHF', 'categories': { '1': { 'price': { '1':25, '2':35 }, 'bg': 'black', 'fg': 'white', 'name': 'Category 1', 'id': 1 }, '2': { 'price': { '1':15, '2':20 }, 'bg': 'green', 'fg': 'white', 'name': 'Category 2', 'id': 2 } }, 'ageGroups': { '1':{ 'id': 1, 'name':'Child', 'age':'0 - 15.99' }, '2':{ 'id': 2, 'name': 'Adult' } }, 'maxTickets': 2 }, event: { 'name': 'TestEvent2', 'location': 'TestLocation2', 'eventID': 'test2', 'date': '2023-07-15', 'currency': 'CHF', 'categories': { '1': { 'price': { '1': 25, '2': 35 }, 'bg': 'black', 'fg': 'white', 'name': 'Category 1', 'id': 1 }, '2': { 'price': { '1': 15, '2': 20 }, 'bg': 'green', 'fg': 'white', 'name': 'Category 2', 'id': 2 } }, 'ageGroups': { '1': { 'id': 1, 'name': 'Child', 'age': '0 - 15.99' }, '2': { 'id': 2, 'name': 'Adult' } }, 'maxTickets': 2 },
cart: {}, cart: {},
selectedTickets: {}, selectedTickets: {},
maxTickets: 10, maxTickets: 10,
} };
}, },
methods: { methods: {
ticketHandling( id, option, operation ) { ticketHandling( id, option, operation ) {
@@ -93,64 +93,64 @@ export default {
} }
}, },
seatChecks () { seatChecks () {
let self = this; let self = this;
let allSeatsAvailable = true; let allSeatsAvailable = true;
fetch( localStorage.getItem( 'url' ) + '/getAPI/getReservedSeats?event=' + this.event.eventID ).then( res => { fetch( localStorage.getItem( 'url' ) + '/getAPI/getReservedSeats?event=' + this.event.eventID ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
let unavailableSeats = {}; let unavailableSeats = {};
res.json().then( data => { res.json().then( data => {
for ( let seat in data.reserved ) { for ( let seat in data.reserved ) {
if ( data.reserved[ seat ] ) { if ( data.reserved[ seat ] ) {
if ( !unavailableSeats[ data.reserved[ seat ].component ] ) { if ( !unavailableSeats[ data.reserved[ seat ].component ] ) {
unavailableSeats[ data.reserved[ seat ].component ] = {}; unavailableSeats[ data.reserved[ seat ].component ] = {};
}
unavailableSeats[ data.reserved[ seat ].component ][ data.reserved[ seat ].id ] = 'nav';
} }
unavailableSeats[ data.reserved[ seat ].component ][ data.reserved[ seat ].id ] = 'nav';
} }
for ( let seat in data.user ) { }
if ( data.user[ seat ] ) { for ( let seat in data.user ) {
if ( !unavailableSeats[ data.user[ seat ].component ] ) { if ( data.user[ seat ] ) {
unavailableSeats[ data.user[ seat ].component ] = {}; if ( !unavailableSeats[ data.user[ seat ].component ] ) {
} unavailableSeats[ data.user[ seat ].component ] = {};
unavailableSeats[ data.user[ seat ].component ][ data.user[ seat ].id ] = 'sel';
} }
unavailableSeats[ data.user[ seat ].component ][ data.user[ seat ].id ] = 'sel';
} }
}
let tickets = {}; let tickets = {};
if ( this.cart[ this.event.eventID ] ) { if ( this.cart[ this.event.eventID ] ) {
tickets = this.cart[ this.event.eventID ][ 'tickets' ]; tickets = this.cart[ this.event.eventID ][ 'tickets' ];
} }
if ( data.user ) { if ( data.user ) {
for ( let element in tickets ) { for ( let element in tickets ) {
if ( !data.user[ element ] ) { if ( !data.user[ element ] ) {
allSeatsAvailable = false; allSeatsAvailable = false;
if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length > 1 ) { if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length > 1 ) {
delete this.cart[ this.event.eventID ][ 'tickets' ][ element ]; delete this.cart[ this.event.eventID ][ 'tickets' ][ element ];
} else { } else {
delete this.cart[ this.event.eventID ]; delete this.cart[ this.event.eventID ];
}
} }
} }
} else {
delete this.cart[ this.event.eventID ];
allSeatsAvailable = false;
} }
} else {
delete this.cart[ this.event.eventID ];
allSeatsAvailable = false;
}
this.unavailableSeats = unavailableSeats; this.unavailableSeats = unavailableSeats;
if ( !allSeatsAvailable ) { if ( !allSeatsAvailable ) {
setTimeout( () => { setTimeout( () => {
self.$refs.popups.openPopup( 'We are sorry to tell you that since the last time the seat plan was refreshed, one or more of the seats you have selected has/have been taken.', {}, 'string' ); self.$refs.popups.openPopup( 'We are sorry to tell you that since the last time the seat plan was refreshed, one or more of the seats you have selected has/have been taken.', {}, 'string' );
}, 500 ); }, 500 );
localStorage.setItem( 'cart', JSON.stringify( this.cart ) ); localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
} }
} ); } );
} else { } else {
console.error( 'unable to load' ); console.error( 'unable to load' );
} }
} ); } );
}, },
cartHandling () { cartHandling () {
for ( let ticket in this.selectedTickets ) { for ( let ticket in this.selectedTickets ) {
@@ -245,7 +245,7 @@ export default {
this.loadTickets(); this.loadTickets();
this.seatChecks(); this.seatChecks();
} }
} };
</script> </script>
<style scoped> <style scoped>

View File

@@ -15,113 +15,113 @@
</template> </template>
<script> <script>
export default { export default {
name: 'notifications', name: 'notifications',
props: { props: {
location: { location: {
type: String, type: String,
'default': 'topleft', 'default': 'topleft',
},
size: {
type: String,
'default': 'default',
}
// Size options: small, default (default option), big, bigger, huge
}, },
data () { size: {
return { type: String,
notifications: {}, 'default': 'default',
queue: [], }
message: '', // Size options: small, default (default option), big, bigger, huge
messageType: 'hide', },
notificationDisplayTime: 0, data () {
notificationPriority: 'normal', return {
currentlyDisplayedNotificationID: 0, notifications: {},
currentID: { 'critical': 0, 'medium': 1000, 'low': 100000 }, queue: [],
displayTimeCurrentNotification: 0, message: '',
notificationScheduler: null, messageType: 'hide',
} notificationDisplayTime: 0,
}, notificationPriority: 'normal',
methods: { currentlyDisplayedNotificationID: 0,
createNotification( message, showDuration, messageType, priority ) { currentID: { 'critical': 0, 'medium': 1000, 'low': 100000 },
/* displayTimeCurrentNotification: 0,
notificationScheduler: null,
};
},
methods: {
createNotification( message, showDuration, messageType, priority ) {
/*
Takes a notification options array that contains: message, showDuration (in seconds), messageType (ok, error, progress, info) and priority (low, normal, critical). Takes a notification options array that contains: message, showDuration (in seconds), messageType (ok, error, progress, info) and priority (low, normal, critical).
Returns a notification ID which can be used to cancel the notification. The component will throttle notifications and display Returns a notification ID which can be used to cancel the notification. The component will throttle notifications and display
one at a time and prioritize messages with higher priority. Use vue refs to access these methods. one at a time and prioritize messages with higher priority. Use vue refs to access these methods.
*/ */
let id = 0; let id = 0;
if ( priority === 'critical' ) { if ( priority === 'critical' ) {
this.currentID[ 'critical' ] += 1; this.currentID[ 'critical' ] += 1;
id = this.currentID[ 'critical' ]; id = this.currentID[ 'critical' ];
} else if ( priority === 'normal' ) { } else if ( priority === 'normal' ) {
this.currentID[ 'medium' ] += 1; this.currentID[ 'medium' ] += 1;
id = this.currentID[ 'medium' ]; id = this.currentID[ 'medium' ];
} else if ( priority === 'low' ) { } else if ( priority === 'low' ) {
this.currentID[ 'low' ] += 1; this.currentID[ 'low' ] += 1;
id = this.currentID[ 'low' ]; id = this.currentID[ 'low' ];
} }
this.notifications[ id ] = { 'message': message, 'showDuration': showDuration, 'messageType': messageType, 'priority': priority, 'id': id }; this.notifications[ id ] = { 'message': message, 'showDuration': showDuration, 'messageType': messageType, 'priority': priority, 'id': id };
this.queue.push( id ); this.queue.push( id );
console.log( 'scheduled notification: ' + id + ' (' + message + ')' ); console.log( 'scheduled notification: ' + id + ' (' + message + ')' );
if ( this.displayTimeCurrentNotification >= this.notificationDisplayTime ) { if ( this.displayTimeCurrentNotification >= this.notificationDisplayTime ) {
this.handleNotifications(); this.handleNotifications();
} }
return id; return id;
}, },
cancelNotification ( id ) { cancelNotification ( id ) {
/* /*
This method deletes a notification and, in case the notification is being displayed, hides it. This method deletes a notification and, in case the notification is being displayed, hides it.
*/ */
try { try {
delete this.notifications[ id ]; delete this.notifications[ id ];
} catch ( error ) { } catch ( error ) {
console.log( 'notification to be deleted is nonexistent or currently being displayed' ); console.log( 'notification to be deleted is nonexistent or currently being displayed' );
} }
try { try {
this.queue.splice( this.queue.indexOf( id ), 1 ); this.queue.splice( this.queue.indexOf( id ), 1 );
} catch {} } catch {}
if ( this.currentlyDisplayedNotificationID == id ) { if ( this.currentlyDisplayedNotificationID == id ) {
this.handleNotifications(); this.handleNotifications();
}
},
handleNotifications () {
/*
This methods should NOT be called in any other component than this one!
*/
this.displayTimeCurrentNotification = 0;
this.notificationDisplayTime = 0;
this.message = '';
this.queue.sort();
if ( this.queue.length > 0 ) {
this.message = this.notifications[ this.queue[ 0 ] ][ 'message' ];
this.messageType = this.notifications[ this.queue[ 0 ] ][ 'messageType' ];
this.priority = this.notifications[ this.queue[ 0 ] ][ 'priority' ];
this.currentlyDisplayedNotificationID = this.notifications[ this.queue[ 0 ] ][ 'id' ];
this.notificationDisplayTime = this.notifications[ this.queue[ 0 ] ][ 'showDuration' ];
delete this.notifications[ this.queue[ 0 ] ];
this.queue.reverse();
this.queue.pop();
$( '.message-box' ).css( 'z-index', 20 );
} else {
this.messageType = 'hide';
$( '.message-box' ).css( 'z-index', -1 );
}
} }
}, },
created () { handleNotifications () {
this.notificationScheduler = setInterval( () => { /*
if ( this.displayTimeCurrentNotification >= this.notificationDisplayTime ) { This methods should NOT be called in any other component than this one!
this.handleNotifications(); */
} else { this.displayTimeCurrentNotification = 0;
this.displayTimeCurrentNotification += 0.5; this.notificationDisplayTime = 0;
} this.message = '';
}, 500 ); this.queue.sort();
}, if ( this.queue.length > 0 ) {
unmounted ( ) { this.message = this.notifications[ this.queue[ 0 ] ][ 'message' ];
clearInterval( this.notificationScheduler ); this.messageType = this.notifications[ this.queue[ 0 ] ][ 'messageType' ];
this.priority = this.notifications[ this.queue[ 0 ] ][ 'priority' ];
this.currentlyDisplayedNotificationID = this.notifications[ this.queue[ 0 ] ][ 'id' ];
this.notificationDisplayTime = this.notifications[ this.queue[ 0 ] ][ 'showDuration' ];
delete this.notifications[ this.queue[ 0 ] ];
this.queue.reverse();
this.queue.pop();
$( '.message-box' ).css( 'z-index', 20 );
} else {
this.messageType = 'hide';
$( '.message-box' ).css( 'z-index', -1 );
}
} }
},
created () {
this.notificationScheduler = setInterval( () => {
if ( this.displayTimeCurrentNotification >= this.notificationDisplayTime ) {
this.handleNotifications();
} else {
this.displayTimeCurrentNotification += 0.5;
}
}, 500 );
},
unmounted ( ) {
clearInterval( this.notificationScheduler );
} }
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -93,81 +93,81 @@
<!-- Options to be passed in: html, settings (for settings component), strings, confirm, dropdowns, selection --> <!-- Options to be passed in: html, settings (for settings component), strings, confirm, dropdowns, selection -->
<script> <script>
import settings from '@/components/settings/settings.vue'; import settings from '@/components/settings/settings.vue';
export default { export default {
name: 'popups', name: 'popups',
components: { components: {
settings, settings,
},
props: {
size: {
type: String,
'default': 'normal',
}, },
props: { },
size: { data () {
type: String, return {
'default': 'normal', contentType: 'dropdown',
}, data: {},
}, info: '',
data () { };
return { },
contentType: 'dropdown', methods: {
data: {}, closePopup( message ) {
info: '', if ( this.data.options.disallowedCharacters ) {
} for ( let letter in this.data.selected ) {
}, if ( this.data.options.disallowedCharacters.includes( this.data.selected[ letter ] ) ) {
methods: { this.info = `Illegal character "${ this.data.selected[ letter ] }"`;
closePopup( message ) { return false;
if ( this.data.options.disallowedCharacters ) {
for ( let letter in this.data.selected ) {
if ( this.data.options.disallowedCharacters.includes( this.data.selected[ letter ] ) ) {
this.info = `Illegal character "${ this.data.selected[ letter ] }"`;
return false;
}
} }
} }
$( '#popup-backdrop' ).fadeOut( 300 ); }
if ( message ) { $( '#popup-backdrop' ).fadeOut( 300 );
this.$emit( 'data', { 'data': this.data.selected, 'status': message } ); if ( message ) {
} this.$emit( 'data', { 'data': this.data.selected, 'status': message } );
}, }
selectTicket ( option ) { },
let total = 0; selectTicket ( option ) {
for ( let i in this.data.options.count ) { let total = 0;
total += this.data.options.count[ i ]; for ( let i in this.data.options.count ) {
} total += this.data.options.count[ i ];
}
if ( total < this.data.options.max ) { if ( total < this.data.options.max ) {
this.data.options.count[ option ] += 1; this.data.options.count[ option ] += 1;
}
},
deselectTicket ( option ) {
if ( this.data.options.count[ option ] > 0 ) {
this.data.options.count[ option ] -= 1;
}
},
submitTicket () {
$( '#popup-backdrop' ).fadeOut( 300 );
this.$emit( 'ticket', { 'data': this.data.options.count, 'component': this.data.options.id } );
},
closePopupAdvanced ( message, data ) {
this.data[ 'selected' ] = data;
this.closePopup( message );
},
submitSettings () {
$( '#popup-backdrop' ).fadeOut( 300 );
const dat = this.data.options;
let ret = {};
for ( let setting in dat ) {
if ( dat[ setting ][ 'type' ] !== 'link' ) {
ret[ setting ] = dat[ setting ][ 'value' ];
}
}
this.$emit( 'data', { 'data': ret, 'status': 'settings' } );
},
openPopup ( message, options, dataType, selected ) {
this.data = { 'message': message ?? 'No message defined on method call!!', 'options': options ?? { '1': { 'value': 'undefined', 'displayName': 'No options specified in call' } }, 'selected': selected ?? '' };
this.contentType = dataType ?? 'string';
$( '#popup-backdrop' ).fadeIn( 300 );
} }
}, },
} deselectTicket ( option ) {
if ( this.data.options.count[ option ] > 0 ) {
this.data.options.count[ option ] -= 1;
}
},
submitTicket () {
$( '#popup-backdrop' ).fadeOut( 300 );
this.$emit( 'ticket', { 'data': this.data.options.count, 'component': this.data.options.id } );
},
closePopupAdvanced ( message, data ) {
this.data[ 'selected' ] = data;
this.closePopup( message );
},
submitSettings () {
$( '#popup-backdrop' ).fadeOut( 300 );
const dat = this.data.options;
let ret = {};
for ( let setting in dat ) {
if ( dat[ setting ][ 'type' ] !== 'link' ) {
ret[ setting ] = dat[ setting ][ 'value' ];
}
}
this.$emit( 'data', { 'data': ret, 'status': 'settings' } );
},
openPopup ( message, options, dataType, selected ) {
this.data = { 'message': message ?? 'No message defined on method call!!', 'options': options ?? { '1': { 'value': 'undefined', 'displayName': 'No options specified in call' } }, 'selected': selected ?? '' };
this.contentType = dataType ?? 'string';
$( '#popup-backdrop' ).fadeIn( 300 );
}
},
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -200,34 +200,34 @@ export default {
props: { props: {
draggables: { draggables: {
type: Object, type: Object,
"default": {} 'default': {}
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
generalSettings: { generalSettings: {
type: Object, type: Object,
"default": {}, 'default': {},
}, },
zoomFactor: { zoomFactor: {
type: Number, type: Number,
"default": 1 'default': 1
}, },
active: { active: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
historyPos: { historyPos: {
type: Number, type: Number,
"default": 0, 'default': 0,
} }
}, },
data () { data () {
return { return {
internal: {}, internal: {},
categories: { '1':{} }, categories: { '1': {} },
} };
}, },
methods: { methods: {
loadInternal () { loadInternal () {
@@ -282,7 +282,7 @@ export default {
created () { created () {
this.loadInternal(); this.loadInternal();
} }
} };
</script> </script>
<style scoped> <style scoped>

View File

@@ -47,47 +47,47 @@
</template> </template>
<script> <script>
import Vue3DraggableResizable from 'vue3-draggable-resizable'; import Vue3DraggableResizable from 'vue3-draggable-resizable';
import properties from '@/components/seatplan/editor/properties.vue'; import properties from '@/components/seatplan/editor/properties.vue';
import circularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/circular.vue'; import circularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/circular.vue';
import rectangularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/rectangular.vue'; import rectangularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/rectangular.vue';
import trapezoidSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/trapezoid.vue'; import trapezoidSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/trapezoid.vue';
import stagesSeatplanComponent from '@/components/seatplan/seatplanComponents/stages.vue'; import stagesSeatplanComponent from '@/components/seatplan/seatplanComponents/stages.vue';
import standingSeatplanComponent from '@/components/seatplan/seatplanComponents/standing.vue'; import standingSeatplanComponent from '@/components/seatplan/seatplanComponents/standing.vue';
import textFieldSeatplanComponent from '@/components/seatplan/seatplanComponents/textField.vue'; import textFieldSeatplanComponent from '@/components/seatplan/seatplanComponents/textField.vue';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css'; import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css';
export default { export default {
'name': 'window', 'name': 'window',
components: { components: {
Vue3DraggableResizable, Vue3DraggableResizable,
properties, properties,
circularSeatplanComponent, circularSeatplanComponent,
rectangularSeatplanComponent, rectangularSeatplanComponent,
trapezoidSeatplanComponent, trapezoidSeatplanComponent,
stagesSeatplanComponent, stagesSeatplanComponent,
standingSeatplanComponent, standingSeatplanComponent,
textFieldSeatplanComponent, textFieldSeatplanComponent,
notifications, notifications,
}, },
data() { data() {
return { return {
active: 0, active: 0,
draggables: { 1: { 'x': 100, 'y':100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': 1, 'origin': 1, 'shape':'rectangular', 'type': 'seat', 'startingRow': 1, 'seatNumberingPosition': 1, 'sector': 'A', 'text': { 'text': 'TestText', 'textSize': 20, 'colour': '#20FFFF' }, 'numberingDirection': 'left', 'seatNumberingPosition': 1, 'category': '1' } }, draggables: { 1: { 'x': 100, 'y': 100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': 1, 'origin': 1, 'shape': 'rectangular', 'type': 'seat', 'startingRow': 1, 'seatNumberingPosition': 1, 'sector': 'A', 'text': { 'text': 'TestText', 'textSize': 20, 'colour': '#20FFFF' }, 'numberingDirection': 'left', 'seatNumberingPosition': 1, 'category': '1' } },
available: { 'redo': false, 'undo': false }, available: { 'redo': false, 'undo': false },
scaleFactor: 1, scaleFactor: 1,
sizePoll: null, sizePoll: null,
prevSize: { 'h': window.innerHeight, 'w': window.innerWidth }, prevSize: { 'h': window.innerHeight, 'w': window.innerWidth },
zoomFactor: 1, zoomFactor: 1,
historyPos: 0, historyPos: 0,
generalSettings: { 'namingScheme': 'numeric' }, generalSettings: { 'namingScheme': 'numeric' },
seatCountInfo: { 'details': {}, 'count': 0 }, seatCountInfo: { 'details': {}, 'count': 0 },
autoSave: null, autoSave: null,
} };
}, },
methods: { methods: {
/* /*
Coords are from top left corner of box. Coords are from top left corner of box.
The below function is executed as the init hook (created hook) The below function is executed as the init hook (created hook)
of vue.js, so whenever this particular page is loaded. of vue.js, so whenever this particular page is loaded.
@@ -96,355 +96,355 @@
browser that meets all the requirements for being able to use the editor browser that meets all the requirements for being able to use the editor
reliably according to testing done. reliably according to testing done.
*/ */
runHook () { runHook () {
if ( !sessionStorage.getItem( 'locationID' ) ) { if ( !sessionStorage.getItem( 'locationID' ) ) {
this.$router.push( '/admin/events' ); this.$router.push( '/admin/events' );
} }
let self = this; let self = this;
this.zoomFactor = sessionStorage.getItem( 'zoom' ) ? parseFloat( sessionStorage.getItem( 'zoom' ) ) : 1; this.zoomFactor = sessionStorage.getItem( 'zoom' ) ? parseFloat( sessionStorage.getItem( 'zoom' ) ) : 1;
/* /*
Keybinds: Keybinds:
- Delete: delete selected object - Delete: delete selected object
- Ctrl + S: Save - Ctrl + S: Save
- Ctrl + Z: Undo - Ctrl + Z: Undo
- Ctrl + Y: Redo - Ctrl + Y: Redo
*/ */
document.onkeydown = function ( event ) { document.onkeydown = function ( event ) {
if ( event.key === 'Delete' ) { if ( event.key === 'Delete' ) {
event.preventDefault(); event.preventDefault();
self.deleteSelected(); self.deleteSelected();
} else if ( event.ctrlKey && event.key === 's' ) { } else if ( event.ctrlKey && event.key === 's' ) {
event.preventDefault(); event.preventDefault();
self.saveDraft(); self.saveDraft();
} else if ( ( event.ctrlKey && event.key === 'y' ) ) { } else if ( ( event.ctrlKey && event.key === 'y' ) ) {
event.preventDefault(); event.preventDefault();
self.historyOp( 'redo' ); self.historyOp( 'redo' );
} else if ( event.ctrlKey && event.key === 'z' ) { } else if ( event.ctrlKey && event.key === 'z' ) {
event.preventDefault(); event.preventDefault();
self.historyOp( 'undo' ); self.historyOp( 'undo' );
} else if ( event.ctrlKey && event.key === 'i' ) { } else if ( event.ctrlKey && event.key === 'i' ) {
event.preventDefault(); event.preventDefault();
self.addNewElement(); self.addNewElement();
} else if ( event.key === '+' ) { } else if ( event.key === '+' ) {
self.zoom( 0.2 ); self.zoom( 0.2 );
} else if ( event.key === '-' ) { } else if ( event.key === '-' ) {
self.zoom( -0.2 ); self.zoom( -0.2 );
} else if ( event.key === '=' ) { } else if ( event.key === '=' ) {
self.zoom( 1 ); self.zoom( 1 );
}
};
// Auto save every 60s (60K ms)
this.autoSave = setInterval( () => {
const options = {
method: 'post',
body: JSON.stringify( { 'data': { 'seatInfo': this.seatCountInfo, 'data': this.scaleDown( this.draggables ) }, 'location': sessionStorage.getItem( 'locationID' ) } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
} }
}; };
fetch( localStorage.getItem( 'url' ) + '/admin/api/saveSeatplanDraft', options );
}, 60000 );
// Auto save every 60s (60K ms) /*
this.autoSave = setInterval( () => {
const options = {
method: 'post',
body: JSON.stringify( { 'data':{ 'seatInfo': this.seatCountInfo, 'data': this.scaleDown( this.draggables ) }, 'location': sessionStorage.getItem( 'locationID' ) } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/api/saveSeatplanDraft', options );
}, 60000 );
/*
Calculate scale factor (this adds support for differently sized screens) Calculate scale factor (this adds support for differently sized screens)
900px is the "default" height 900px is the "default" height
*/ */
let height = $( document ).height() * 0.8; let height = $( document ).height() * 0.8;
this.scaleFactor = ( height / 900 ) * this.zoomFactor; this.scaleFactor = ( height / 900 ) * this.zoomFactor;
/* /*
Load seatplan Load seatplan
*/ */
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getSeatplanDraft?location=' + sessionStorage.getItem( 'locationID' ) ).then( res => { fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getSeatplanDraft?location=' + sessionStorage.getItem( 'locationID' ) ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
res.json().then( data => { res.json().then( data => {
this.draggables = this.scaleUp( data.data ); this.draggables = this.scaleUp( data.data );
sessionStorage.setItem( 'seatplan', JSON.stringify( data.data ) ); sessionStorage.setItem( 'seatplan', JSON.stringify( data.data ) );
for ( let element in this.draggables ) {
if ( this.draggables[ element ].active ) {
this.draggables[ element ].active = false;
}
}
} );
} else if ( res.status === 500 ) {
if ( sessionStorage.getItem( 'seatplan' ) ) {
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan' ) ) );
}
for ( let element in this.draggables ) { for ( let element in this.draggables ) {
if ( this.draggables[ element ].active ) { if ( this.draggables[ element ].active ) {
this.draggables[ element ].active = false; this.draggables[ element ].active = false;
} }
} }
} );
} else if ( res.status === 500 ) {
if ( sessionStorage.getItem( 'seatplan' ) ) {
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan' ) ) );
}
for ( let element in this.draggables ) {
if ( this.draggables[ element ].active ) {
this.draggables[ element ].active = false;
}
} }
} );
if ( !sessionStorage.getItem( 'seatplan-history' ) ) {
sessionStorage.setItem( 'seatplan-history', JSON.stringify( { '1': this.scaleDown( this.draggables ) } ) );
} }
} );
let history = sessionStorage.getItem( 'seatplan-history' ) ? JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) : {}; if ( !sessionStorage.getItem( 'seatplan-history' ) ) {
let count = parseInt( Object.keys( history ).length ); sessionStorage.setItem( 'seatplan-history', JSON.stringify( { '1': this.scaleDown( this.draggables ) } ) );
}
if ( count > parseInt( sessionStorage.getItem( 'historyPos' ) ) ) { let history = sessionStorage.getItem( 'seatplan-history' ) ? JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) : {};
this.available.redo = true; let count = parseInt( Object.keys( history ).length );
}
if ( parseInt( sessionStorage.getItem( 'historyPos' ) ) > 0 ) { if ( count > parseInt( sessionStorage.getItem( 'historyPos' ) ) ) {
this.available.undo = true; this.available.redo = true;
} }
// if ( window.webpage.engine.trident ) { if ( parseInt( sessionStorage.getItem( 'historyPos' ) ) > 0 ) {
// alert( 'Welcome! We have detected that you are still using Internet Explorer or a similar browser. As a modern webapp, libreevent does NOT officially support Internet Explorer. If you run into problems whilst using this webapp, please switch to a modern browser like Firefox.' ); this.available.undo = true;
// } else if ( window.webpage.engine.presto ) { }
// alert( 'Welcome! We have detected that you are a very old version of Opera or related browser. As a modern webapp, libreevent does only support modern browsers. If you run into issues whilst using this webapp, please switch to a modern browser, like Firefox.' );
// } else if ( window.webpage.engine.webkit ) { // if ( window.webpage.engine.trident ) {
// alert( 'Hello! Whilst tested with some versions of Webkit (the browser engine of Safari), support for this engine is still unofficial. Therefore we cannot guarantee that all the features of the seatplan editor function as they should. If you run into problems, please contact us through the link provided in the documentation.' ); // alert( 'Welcome! We have detected that you are still using Internet Explorer or a similar browser. As a modern webapp, libreevent does NOT officially support Internet Explorer. If you run into problems whilst using this webapp, please switch to a modern browser like Firefox.' );
// } // } else if ( window.webpage.engine.presto ) {
this.save(); // alert( 'Welcome! We have detected that you are a very old version of Opera or related browser. As a modern webapp, libreevent does only support modern browsers. If you run into issues whilst using this webapp, please switch to a modern browser, like Firefox.' );
}, // } else if ( window.webpage.engine.webkit ) {
eventHandler ( e ) { // alert( 'Hello! Whilst tested with some versions of Webkit (the browser engine of Safari), support for this engine is still unofficial. Therefore we cannot guarantee that all the features of the seatplan editor function as they should. If you run into problems, please contact us through the link provided in the documentation.' );
if ( this.prevSize.h != window.innerHeight || this.prevSize.w != window.innerWidth ) { // }
this.prevSize = { 'h': window.innerHeight, 'w': window.innerWidth }; this.save();
this.loadSeatplan(); },
} eventHandler ( e ) {
}, if ( this.prevSize.h != window.innerHeight || this.prevSize.w != window.innerWidth ) {
loadSeatplan () { this.prevSize = { 'h': window.innerHeight, 'w': window.innerWidth };
/* this.loadSeatplan();
}
},
loadSeatplan () {
/*
Calculate scale factor (this adds support for differently sized screens) Calculate scale factor (this adds support for differently sized screens)
900px is the "default" height 900px is the "default" height
*/ */
let height = $( document ).height() * 0.8; let height = $( document ).height() * 0.8;
this.scaleFactor = ( height / 900 ) * this.zoomFactor; this.scaleFactor = ( height / 900 ) * this.zoomFactor;
if ( sessionStorage.getItem( 'seatplan' ) ) { if ( sessionStorage.getItem( 'seatplan' ) ) {
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan' ) ) ); this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan' ) ) );
} }
for ( let element in this.draggables ) { for ( let element in this.draggables ) {
if ( this.draggables[ element ].active ) { if ( this.draggables[ element ].active ) {
this.draggables[ element ].active = false; this.draggables[ element ].active = false;
}
} }
}, }
scaleDown ( valueArray ) { },
const allowedAttributes = [ 'w', 'h', 'x', 'y' ] scaleDown ( valueArray ) {
let returnArray = {}; const allowedAttributes = [ 'w', 'h', 'x', 'y' ];
for ( let entry in valueArray ) { let returnArray = {};
returnArray[ entry ] = {}; for ( let entry in valueArray ) {
for ( let attributes in valueArray[ entry ] ) { returnArray[ entry ] = {};
if ( allowedAttributes.includes( attributes ) ) { for ( let attributes in valueArray[ entry ] ) {
returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] / this.scaleFactor ) * 1000 ) / 1000; if ( allowedAttributes.includes( attributes ) ) {
} else { returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] / this.scaleFactor ) * 1000 ) / 1000;
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
}
}
}
return returnArray;
},
scaleUp ( valueArray ) {
const allowedAttributes = [ 'w', 'h', 'x', 'y' ]
let returnArray = {};
for ( let entry in valueArray ) {
returnArray[ entry ] = {};
for ( let attributes in valueArray[ entry ] ) {
if ( allowedAttributes.includes( attributes ) ) {
returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] * this.scaleFactor ) * 1000 ) / 1000;
} else {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
}
}
}
return returnArray;
},
activateComponent ( id ) {
this.active = id;
},
saveHistory () {
let history = sessionStorage.getItem( 'seatplan-history' ) ? JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) : {};
let count = parseInt( Object.keys( history ).length + 1 );
this.historyPos = count;
if ( count - 1 > parseInt( sessionStorage.getItem( 'historyPos' ) ) ) {
for ( let i = parseInt( sessionStorage.getItem( 'historyPos' ) ) + 1; i < count; i++ ) {
delete history[ i ];
this.available.redo = false;
}
}
count = parseInt( Object.keys( history ).length + 1 );
sessionStorage.setItem( 'historyPos', count );
history[ count ] = this.scaleDown( this.draggables );
sessionStorage.setItem( 'seatplan-history', JSON.stringify( history ) );
if ( parseInt( sessionStorage.getItem( 'historyPos' ) ) > 1 ) {
this.available.undo = true;
}
this.save();
},
historyOp ( action ) {
if ( action === 'undo' ) {
if ( parseInt( sessionStorage.getItem( 'historyPos' ) ) > 1 ) {
sessionStorage.setItem( 'historyPos', parseInt( sessionStorage.getItem( 'historyPos' ) ) - 1 );
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan-history' ) )[ sessionStorage.getItem( 'historyPos' ) ] );
this.available.redo = true;
if ( parseInt( sessionStorage.getItem( 'historyPos' ) ) < 2 ) {
this.available.undo = false;
}
} else { } else {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
}
}
}
return returnArray;
},
scaleUp ( valueArray ) {
const allowedAttributes = [ 'w', 'h', 'x', 'y' ];
let returnArray = {};
for ( let entry in valueArray ) {
returnArray[ entry ] = {};
for ( let attributes in valueArray[ entry ] ) {
if ( allowedAttributes.includes( attributes ) ) {
returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] * this.scaleFactor ) * 1000 ) / 1000;
} else {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
}
}
}
return returnArray;
},
activateComponent ( id ) {
this.active = id;
},
saveHistory () {
let history = sessionStorage.getItem( 'seatplan-history' ) ? JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) : {};
let count = parseInt( Object.keys( history ).length + 1 );
this.historyPos = count;
if ( count - 1 > parseInt( sessionStorage.getItem( 'historyPos' ) ) ) {
for ( let i = parseInt( sessionStorage.getItem( 'historyPos' ) ) + 1; i < count; i++ ) {
delete history[ i ];
this.available.redo = false;
}
}
count = parseInt( Object.keys( history ).length + 1 );
sessionStorage.setItem( 'historyPos', count );
history[ count ] = this.scaleDown( this.draggables );
sessionStorage.setItem( 'seatplan-history', JSON.stringify( history ) );
if ( parseInt( sessionStorage.getItem( 'historyPos' ) ) > 1 ) {
this.available.undo = true;
}
this.save();
},
historyOp ( action ) {
if ( action === 'undo' ) {
if ( parseInt( sessionStorage.getItem( 'historyPos' ) ) > 1 ) {
sessionStorage.setItem( 'historyPos', parseInt( sessionStorage.getItem( 'historyPos' ) ) - 1 );
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan-history' ) )[ sessionStorage.getItem( 'historyPos' ) ] );
this.available.redo = true;
if ( parseInt( sessionStorage.getItem( 'historyPos' ) ) < 2 ) {
this.available.undo = false; this.available.undo = false;
} }
} else if ( action === 'redo' ) { } else {
if ( parseInt( Object.keys( JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) ).length ) > parseInt( sessionStorage.getItem( 'historyPos' ) ) ) { this.available.undo = false;
sessionStorage.setItem( 'historyPos', parseInt( sessionStorage.getItem( 'historyPos' ) ) + 1 ); }
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan-history' ) )[ sessionStorage.getItem( 'historyPos' ) ] ); } else if ( action === 'redo' ) {
this.available.undo = true; if ( parseInt( Object.keys( JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) ).length ) > parseInt( sessionStorage.getItem( 'historyPos' ) ) ) {
if ( parseInt( Object.keys( JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) ).length ) < parseInt( sessionStorage.getItem( 'historyPos' ) ) + 1 ) { sessionStorage.setItem( 'historyPos', parseInt( sessionStorage.getItem( 'historyPos' ) ) + 1 );
this.available.redo = false; this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan-history' ) )[ sessionStorage.getItem( 'historyPos' ) ] );
} this.available.undo = true;
} else { if ( parseInt( Object.keys( JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) ).length ) < parseInt( sessionStorage.getItem( 'historyPos' ) ) + 1 ) {
this.available.redo = false; this.available.redo = false;
} }
} else {
this.available.redo = false;
} }
this.historyPos = sessionStorage.getItem( 'historyPos' ); }
}, this.historyPos = sessionStorage.getItem( 'historyPos' );
save () { },
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) ); save () {
}, sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) );
saveDraft () { },
if ( !this.getSeatCount() ) { saveDraft () {
this.$refs.notification.createNotification( 'Collision of seat count!', 10, 'error', 'normal' ); if ( !this.getSeatCount() ) {
return; this.$refs.notification.createNotification( 'Collision of seat count!', 10, 'error', 'normal' );
return;
}
let progressNotification = this.$refs.notification.createNotification( 'Saving as draft', 5, 'progress', 'normal' );
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) );
const options = {
method: 'post',
body: JSON.stringify( { 'data': { 'seatInfo': this.seatCountInfo, 'data': this.scaleDown( this.draggables ) }, 'location': sessionStorage.getItem( 'locationID' ) } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
} }
let progressNotification = this.$refs.notification.createNotification( 'Saving as draft', 5, 'progress', 'normal' ); };
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) ); fetch( localStorage.getItem( 'url' ) + '/admin/api/saveSeatplanDraft', options ).then( res => {
const options = { if ( res.status === 200 ) {
method: 'post', res.text().then( () => {
body: JSON.stringify( { 'data':{ 'seatInfo': this.seatCountInfo, 'data': this.scaleDown( this.draggables ) }, 'location': sessionStorage.getItem( 'locationID' ) } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/api/saveSeatplanDraft', options ).then( res => {
if ( res.status === 200 ) {
res.text().then( () => {
this.$refs.notification.cancelNotification( progressNotification );
this.$refs.notification.createNotification( 'Saved as draft', 5, 'ok', 'normal' );
} );
} else if ( res.status === 403 ) {
this.$refs.notification.cancelNotification( progressNotification ); this.$refs.notification.cancelNotification( progressNotification );
this.$refs.notification.createNotification( 'Unauthenticated', 5, 'ok', 'error' ); this.$refs.notification.createNotification( 'Saved as draft', 5, 'ok', 'normal' );
} } );
} ); } else if ( res.status === 403 ) {
}, this.$refs.notification.cancelNotification( progressNotification );
deploy () { this.$refs.notification.createNotification( 'Unauthenticated', 5, 'ok', 'error' );
if ( !this.getSeatCount() ) {
this.$refs.notification.createNotification( 'Collision of seat count!', 10, 'error', 'normal' );
return;
} }
let deployNotification = this.$refs.notification.createNotification( 'Deploying...', 5, 'progress', 'normal' ); } );
const options = { },
method: 'post', deploy () {
body: JSON.stringify( { 'data':{ 'seatInfo': this.seatCountInfo, 'data': this.scaleDown( this.draggables ) }, 'location': sessionStorage.getItem( 'locationID' ) } ), if ( !this.getSeatCount() ) {
headers: { this.$refs.notification.createNotification( 'Collision of seat count!', 10, 'error', 'normal' );
'Content-Type': 'application/json', return;
'charset': 'utf-8' }
} let deployNotification = this.$refs.notification.createNotification( 'Deploying...', 5, 'progress', 'normal' );
}; const options = {
fetch( localStorage.getItem( 'url' ) + '/admin/api/saveSeatplan', options ).then( res => { method: 'post',
if ( res.status === 200 ) { body: JSON.stringify( { 'data': { 'seatInfo': this.seatCountInfo, 'data': this.scaleDown( this.draggables ) }, 'location': sessionStorage.getItem( 'locationID' ) } ),
res.text().then( () => { headers: {
this.$refs.notification.cancelNotification( deployNotification ); 'Content-Type': 'application/json',
this.$refs.notification.createNotification( 'Deployed successfully', 5, 'ok', 'normal' ); 'charset': 'utf-8'
} ); }
} else if ( res.status === 403 ) { };
fetch( localStorage.getItem( 'url' ) + '/admin/api/saveSeatplan', options ).then( res => {
if ( res.status === 200 ) {
res.text().then( () => {
this.$refs.notification.cancelNotification( deployNotification ); this.$refs.notification.cancelNotification( deployNotification );
this.$refs.notification.createNotification( 'Unauthenticated', 5, 'ok', 'error' ); this.$refs.notification.createNotification( 'Deployed successfully', 5, 'ok', 'normal' );
} } );
} ); } else if ( res.status === 403 ) {
}, this.$refs.notification.cancelNotification( deployNotification );
addNewElement () { this.$refs.notification.createNotification( 'Unauthenticated', 5, 'ok', 'error' );
this.draggables[ Object.keys( this.draggables ).length + 1 ] = { 'x': 100, 'y':100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': ( Object.keys( this.draggables ).length + 1 ), 'origin': 1, 'shape':'rectangular', 'type': 'seat', 'startingRow': 1, 'seatNumberingPosition': Object.keys( this.draggables ).length, 'sector': 'A', 'text': { 'text': 'TestText', 'textSize': 20, 'colour': '#20FFFF' }, 'ticketCount': 1, 'numberingDirection': 'left', 'category': '1' };
this.saveHistory();
document.getElementById( 'parent' ).scrollTop = 0;
document.getElementById( 'parent' ).scrollLeft = 0;
this.$refs.notification.createNotification( 'New component added successfully', 5, 'ok', 'normal' );
},
deleteSelected () {
if ( this.active ) {
this.draggables[ this.active ].active = true;
if ( confirm( 'Do you really want to delete the selected item?' ) ) {
delete this.draggables[ this.active ];
this.saveHistory();
this.active = 0;
this.$refs.notification.createNotification( 'Successfully deleted component', 5, 'ok', 'normal' );
}
} else {
this.$refs.notification.createNotification( 'Please select a seat first!', 5, 'error', 'normal' );
} }
}, } );
handleUpdate ( value ) { },
this.draggables = value; addNewElement () {
this.selectedObject = value; this.draggables[ Object.keys( this.draggables ).length + 1 ] = { 'x': 100, 'y': 100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': ( Object.keys( this.draggables ).length + 1 ), 'origin': 1, 'shape': 'rectangular', 'type': 'seat', 'startingRow': 1, 'seatNumberingPosition': Object.keys( this.draggables ).length, 'sector': 'A', 'text': { 'text': 'TestText', 'textSize': 20, 'colour': '#20FFFF' }, 'ticketCount': 1, 'numberingDirection': 'left', 'category': '1' };
this.saveHistory(); this.saveHistory();
}, document.getElementById( 'parent' ).scrollTop = 0;
zoom ( scale ) { document.getElementById( 'parent' ).scrollLeft = 0;
if ( scale == 1 ) { this.$refs.notification.createNotification( 'New component added successfully', 5, 'ok', 'normal' );
this.zoomFactor = 1; },
sessionStorage.setItem( 'zoom', this.zoomFactor ); deleteSelected () {
this.loadSeatplan(); if ( this.active ) {
} else { this.draggables[ this.active ].active = true;
if ( ( this.zoomFactor < 0.3 && scale < 0 ) || ( this.zoomFactor > 2.9 && scale > 0 ) ) { if ( confirm( 'Do you really want to delete the selected item?' ) ) {
if ( this.zoomFactor < 0.3 ) { delete this.draggables[ this.active ];
this.$refs.notification.createNotification( 'Minimum zoom factor reached', 5, 'warning', 'normal' ); this.saveHistory();
} else { this.active = 0;
this.$refs.notification.createNotification( 'Maximum zoom factor reached', 5, 'warning', 'normal' ); this.$refs.notification.createNotification( 'Successfully deleted component', 5, 'ok', 'normal' );
} }
} else {
this.$refs.notification.createNotification( 'Please select a seat first!', 5, 'error', 'normal' );
}
},
handleUpdate ( value ) {
this.draggables = value;
this.selectedObject = value;
this.saveHistory();
},
zoom ( scale ) {
if ( scale == 1 ) {
this.zoomFactor = 1;
sessionStorage.setItem( 'zoom', this.zoomFactor );
this.loadSeatplan();
} else {
if ( ( this.zoomFactor < 0.3 && scale < 0 ) || ( this.zoomFactor > 2.9 && scale > 0 ) ) {
if ( this.zoomFactor < 0.3 ) {
this.$refs.notification.createNotification( 'Minimum zoom factor reached', 5, 'warning', 'normal' );
} else { } else {
this.zoomFactor += scale; this.$refs.notification.createNotification( 'Maximum zoom factor reached', 5, 'warning', 'normal' );
} }
sessionStorage.setItem( 'zoom', this.zoomFactor ); } else {
this.loadSeatplan(); this.zoomFactor += scale;
} }
}, sessionStorage.setItem( 'zoom', this.zoomFactor );
handleSeatCountInfo ( info ) { this.loadSeatplan();
this.seatCountInfo[ 'details' ][ info.id ] = info.data; }
this.seatCountInfo[ 'details' ][ info.id ][ 'startingRow' ] = this.draggables[ info.id ].startingRow; },
}, handleSeatCountInfo ( info ) {
getSeatCount () { this.seatCountInfo[ 'details' ][ info.id ] = info.data;
this.seatCountInfo[ 'count' ] = document.getElementsByClassName( 'seats' ).length; this.seatCountInfo[ 'details' ][ info.id ][ 'startingRow' ] = this.draggables[ info.id ].startingRow;
for ( let draggable in this.draggables ) { },
if ( this.draggables[ draggable ][ 'ticketCount' ] ) { getSeatCount () {
this.seatCountInfo[ 'count' ] += this.draggables[ draggable ][ 'ticketCount' ]; this.seatCountInfo[ 'count' ] = document.getElementsByClassName( 'seats' ).length;
} for ( let draggable in this.draggables ) {
if ( this.draggables[ draggable ][ 'ticketCount' ] ) {
this.seatCountInfo[ 'count' ] += this.draggables[ draggable ][ 'ticketCount' ];
} }
// Remap seat count info }
this.seatCountInfo[ 'data' ] = {}; // Remap seat count info
for ( let element in this.seatCountInfo[ 'details' ] ) { this.seatCountInfo[ 'data' ] = {};
if ( !this.seatCountInfo[ 'data' ][ this.draggables[ 1 ].sector ] ) { for ( let element in this.seatCountInfo[ 'details' ] ) {
this.seatCountInfo[ 'data' ][ this.draggables[ 1 ].sector ] = {}; if ( !this.seatCountInfo[ 'data' ][ this.draggables[ 1 ].sector ] ) {
} this.seatCountInfo[ 'data' ][ this.draggables[ 1 ].sector ] = {};
if ( this.seatCountInfo[ 'data' ][ this.draggables[ 1 ].sector ][ this.draggables[ element ].seatNumberingPosition ] ) return false;
this.seatCountInfo[ 'data' ][ this.draggables[ 1 ].sector ][ this.draggables[ element ].seatNumberingPosition ] = this.seatCountInfo[ 'details' ][ element ];
} }
return true; if ( this.seatCountInfo[ 'data' ][ this.draggables[ 1 ].sector ][ this.draggables[ element ].seatNumberingPosition ] ) return false;
}, this.seatCountInfo[ 'data' ][ this.draggables[ 1 ].sector ][ this.draggables[ element ].seatNumberingPosition ] = this.seatCountInfo[ 'details' ][ element ];
}
return true;
}, },
created () { },
this.runHook(); created () {
this.sizePoll = setInterval( this.eventHandler, 250 ); this.runHook();
}, this.sizePoll = setInterval( this.eventHandler, 250 );
unmounted() { },
clearInterval( this.sizePoll ); unmounted() {
clearInterval( this.autoSave ); clearInterval( this.sizePoll );
}, clearInterval( this.autoSave );
} },
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -27,33 +27,33 @@ export default {
props: { props: {
h: { h: {
type: Number, type: Number,
"default": 100, 'default': 100,
}, },
w: { w: {
type: Number, type: Number,
"default": 200, 'default': 200,
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
startingRow: { startingRow: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
id: { id: {
type: Number, type: Number,
"default": 1 'default': 1
} }
}, },
data () { data () {
return { return {
seats: {}, seats: {},
} };
}, },
methods: { methods: {
calculateChairs () { calculateChairs () {
@@ -114,6 +114,6 @@ export default {
created() { created() {
this.calculateChairs(); this.calculateChairs();
} }
} };
</script> </script>

View File

@@ -27,29 +27,29 @@ export default {
props: { props: {
h: { h: {
type: Number, type: Number,
"default": 100, 'default': 100,
}, },
w: { w: {
type: Number, type: Number,
"default": 200, 'default': 200,
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
id: { id: {
type: Number, type: Number,
"default": 1 'default': 1
} }
}, },
data () { data () {
return { return {
seats: {}, seats: {},
} };
}, },
methods: { methods: {
calculateChairs () { calculateChairs () {
@@ -104,6 +104,6 @@ export default {
created() { created() {
this.calculateChairs(); this.calculateChairs();
} }
} };
</script> </script>

View File

@@ -27,33 +27,33 @@ export default {
props: { props: {
h: { h: {
type: Number, type: Number,
"default": 100, 'default': 100,
}, },
w: { w: {
type: Number, type: Number,
"default": 200, 'default': 200,
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
startingRow: { startingRow: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
id: { id: {
type: Number, type: Number,
"default": 1 'default': 1
} }
}, },
data () { data () {
return { return {
seats: {}, seats: {},
} };
}, },
methods: { methods: {
calculateChairs () { calculateChairs () {
@@ -63,7 +63,7 @@ export default {
let h = Math.round( this.h / this.scaleFactor ); let h = Math.round( this.h / this.scaleFactor );
const size = 33; const size = 33;
let side = Math.min( w, h ) + 20; let side = Math.min( w, h ) + 20;
let heightTriangle = Math.floor( Math.sqrt( side ** 2 - ( Math.sqrt( side ** 2 * 2 ) / 2 ) ) ) let heightTriangle = Math.floor( Math.sqrt( side ** 2 - ( Math.sqrt( side ** 2 * 2 ) / 2 ) ) );
let sideOffset = size / Math.sqrt( 2 ); let sideOffset = size / Math.sqrt( 2 );
let count = Math.floor( heightTriangle / ( sideOffset * 2 ) ); let count = Math.floor( heightTriangle / ( sideOffset * 2 ) );
const angle = Math.PI / 4; const angle = Math.PI / 4;
@@ -117,6 +117,6 @@ export default {
created() { created() {
this.calculateChairs(); this.calculateChairs();
} }
} };
</script> </script>

View File

@@ -66,11 +66,11 @@ export default {
props: { props: {
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
shape: { shape: {
type: String, type: String,
"default": "rectangular", 'default': 'rectangular',
}, },
}, },
data() { data() {
@@ -78,7 +78,7 @@ export default {
style: 'border-style: none none solid none', style: 'border-style: none none solid none',
circularStyle: 'top: 0; left 100%;', circularStyle: 'top: 0; left 100%;',
trapezoidStyle: 'rotate: 45deg', trapezoidStyle: 'rotate: 45deg',
} };
}, },
methods: { methods: {
updateOrigin () { updateOrigin () {
@@ -109,6 +109,6 @@ export default {
created() { created() {
this.updateOrigin(); this.updateOrigin();
} }
} };
</script> </script>

View File

@@ -66,11 +66,11 @@ export default {
props: { props: {
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
shape: { shape: {
type: String, type: String,
"default": "rectangular", 'default': 'rectangular',
}, },
}, },
data() { data() {
@@ -78,7 +78,7 @@ export default {
style: 'border-style: none none solid none', style: 'border-style: none none solid none',
circularStyle: 'top: 0; left 100%;', circularStyle: 'top: 0; left 100%;',
trapezoidStyle: 'rotate: 45deg', trapezoidStyle: 'rotate: 45deg',
} };
}, },
methods: { methods: {
updateOrigin () { updateOrigin () {
@@ -109,6 +109,6 @@ export default {
created() { created() {
this.updateOrigin(); this.updateOrigin();
} }
} };
</script> </script>

View File

@@ -19,29 +19,29 @@ export default {
props: { props: {
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
text: { text: {
type: String, type: String,
"default": "Untitled", 'default': 'Untitled',
}, },
textSize: { textSize: {
type: Number, type: Number,
"default": 20, 'default': 20,
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
colour: { colour: {
type: String, type: String,
"default": '#000000', 'default': '#000000',
} }
}, },
data() { data() {
return { return {
style: 'font-size: 20pt; rotate: 0deg; color: #000000', style: 'font-size: 20pt; rotate: 0deg; color: #000000',
} };
}, },
methods: { methods: {
updateStyle () { updateStyle () {
@@ -65,5 +65,5 @@ export default {
created() { created() {
this.updateStyle(); this.updateStyle();
} }
} };
</script> </script>

View File

@@ -34,37 +34,37 @@ export default {
props: { props: {
h: { h: {
type: Number, type: Number,
"default": 100, 'default': 100,
}, },
w: { w: {
type: Number, type: Number,
"default": 200, 'default': 200,
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
startingRow: { startingRow: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
data: { data: {
type: Object, type: Object,
"default": { 'sector': 'A', 'sectorCount': 1, 'unavailableSeats': { 'secAr0s0': 'nav' }, 'categoryInfo': { 'pricing': { '1': { 'displayName': 'Adults - CHF 20.-', 'value': '1', 'price': 20 }, '2': { 'displayName': 'Child (0 - 15.99y) - CHF 15.-', 'value': '2', 'price': 15 } } } } 'default': { 'sector': 'A', 'sectorCount': 1, 'unavailableSeats': { 'secAr0s0': 'nav' }, 'categoryInfo': { 'pricing': { '1': { 'displayName': 'Adults - CHF 20.-', 'value': '1', 'price': 20 }, '2': { 'displayName': 'Child (0 - 15.99y) - CHF 15.-', 'value': '2', 'price': 15 } } } }
}, },
id: { id: {
type: Number, type: Number,
"default": 1, 'default': 1,
} }
}, },
data () { data () {
return { return {
seats: {}, seats: {},
} };
}, },
methods: { methods: {
calculateChairs () { calculateChairs () {
@@ -175,6 +175,6 @@ export default {
created() { created() {
this.calculateChairs(); this.calculateChairs();
} }
} };
</script> </script>

View File

@@ -34,41 +34,41 @@ export default {
props: { props: {
h: { h: {
type: Number, type: Number,
"default": 100, 'default': 100,
}, },
w: { w: {
type: Number, type: Number,
"default": 200, 'default': 200,
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
startingRow: { startingRow: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
data: { data: {
type: Object, type: Object,
"default": { 'sector': 'A', 'sectorCount': 1, 'unavailableSeats': { 'secAr0s0': 'nav' }, 'categoryInfo': { 'pricing': { '1': { 'displayName': 'Adults - CHF 20.-', 'value': '1', 'price': 20 }, '2': { 'displayName': 'Child (0 - 15.99y) - CHF 15.-', 'value': '2', 'price': 15 } } } } 'default': { 'sector': 'A', 'sectorCount': 1, 'unavailableSeats': { 'secAr0s0': 'nav' }, 'categoryInfo': { 'pricing': { '1': { 'displayName': 'Adults - CHF 20.-', 'value': '1', 'price': 20 }, '2': { 'displayName': 'Child (0 - 15.99y) - CHF 15.-', 'value': '2', 'price': 15 } } } }
}, },
id: { id: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
unavailable: { unavailable: {
type: Object, type: Object,
"default": {} 'default': {}
} }
}, },
data () { data () {
return { return {
seats: {}, seats: {},
} };
}, },
methods: { methods: {
calculateChairs () { calculateChairs () {
@@ -170,6 +170,6 @@ export default {
created() { created() {
this.calculateChairs(); this.calculateChairs();
} }
} };
</script> </script>

View File

@@ -34,37 +34,37 @@ export default {
props: { props: {
h: { h: {
type: Number, type: Number,
"default": 100, 'default': 100,
}, },
w: { w: {
type: Number, type: Number,
"default": 200, 'default': 200,
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
startingRow: { startingRow: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
data: { data: {
type: Object, type: Object,
"default": { 'sector': 'A', 'sectorCount': 1, 'unavailableSeats': { 'secAr0s0': 'nav' }, 'categoryInfo': { 'pricing': { '1': { 'displayName': 'Adults - CHF 20.-', 'value': '1', 'price': 20 }, '2': { 'displayName': 'Child (0 - 15.99y) - CHF 15.-', 'value': '2', 'price': 15 } } } } 'default': { 'sector': 'A', 'sectorCount': 1, 'unavailableSeats': { 'secAr0s0': 'nav' }, 'categoryInfo': { 'pricing': { '1': { 'displayName': 'Adults - CHF 20.-', 'value': '1', 'price': 20 }, '2': { 'displayName': 'Child (0 - 15.99y) - CHF 15.-', 'value': '2', 'price': 15 } } } }
}, },
id: { id: {
type: Number, type: Number,
"default": 1, 'default': 1,
} }
}, },
data () { data () {
return { return {
seats: {}, seats: {},
} };
}, },
methods: { methods: {
calculateChairs () { calculateChairs () {
@@ -87,7 +87,7 @@ export default {
let h = Math.round( this.h / this.scaleFactor ); let h = Math.round( this.h / this.scaleFactor );
const size = 33; const size = 33;
let side = Math.min( w, h ) + 20; let side = Math.min( w, h ) + 20;
let heightTriangle = Math.floor( Math.sqrt( side ** 2 - ( Math.sqrt( side ** 2 * 2 ) / 2 ) ) ) let heightTriangle = Math.floor( Math.sqrt( side ** 2 - ( Math.sqrt( side ** 2 * 2 ) / 2 ) ) );
let sideOffset = size / Math.sqrt( 2 ); let sideOffset = size / Math.sqrt( 2 );
let count = Math.floor( heightTriangle / ( sideOffset * 2 ) ); let count = Math.floor( heightTriangle / ( sideOffset * 2 ) );
const angle = Math.PI / 4; const angle = Math.PI / 4;
@@ -178,6 +178,6 @@ export default {
created() { created() {
this.calculateChairs(); this.calculateChairs();
} }
} };
</script> </script>

View File

@@ -66,11 +66,11 @@ export default {
props: { props: {
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
shape: { shape: {
type: String, type: String,
"default": "rectangular", 'default': 'rectangular',
}, },
}, },
data() { data() {
@@ -78,7 +78,7 @@ export default {
style: 'border-style: none none solid none', style: 'border-style: none none solid none',
circularStyle: 'top: 0; left 100%;', circularStyle: 'top: 0; left 100%;',
trapezoidStyle: 'rotate: 45deg', trapezoidStyle: 'rotate: 45deg',
} };
}, },
methods: { methods: {
updateOrigin () { updateOrigin () {
@@ -109,6 +109,6 @@ export default {
created() { created() {
this.updateOrigin(); this.updateOrigin();
} }
} };
</script> </script>

View File

@@ -65,15 +65,15 @@ export default {
props: { props: {
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
shape: { shape: {
type: String, type: String,
"default": "rectangular", 'default': 'rectangular',
}, },
color: { color: {
type: Object, type: Object,
"default": { 'fg': '#FFFFFF' } 'default': { 'fg': '#FFFFFF' }
} }
}, },
data() { data() {
@@ -81,7 +81,7 @@ export default {
style: 'border-style: none none solid none;', style: 'border-style: none none solid none;',
circularStyle: 'top: 0; left 100%;', circularStyle: 'top: 0; left 100%;',
trapezoidStyle: 'rotate: 45deg', trapezoidStyle: 'rotate: 45deg',
} };
}, },
methods: { methods: {
updateOrigin () { updateOrigin () {
@@ -115,6 +115,6 @@ export default {
created() { created() {
this.updateOrigin(); this.updateOrigin();
} }
} };
</script> </script>

View File

@@ -19,29 +19,29 @@ export default {
props: { props: {
origin: { origin: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
text: { text: {
type: String, type: String,
"default": "Untitled", 'default': 'Untitled',
}, },
textSize: { textSize: {
type: Number, type: Number,
"default": 20, 'default': 20,
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, 'default': 1,
}, },
colour: { colour: {
type: String, type: String,
"default": '#000000', 'default': '#000000',
} }
}, },
data() { data() {
return { return {
style: 'font-size: 20pt; rotate: 0deg; color: #000000', style: 'font-size: 20pt; rotate: 0deg; color: #000000',
} };
}, },
methods: { methods: {
updateStyle () { updateStyle () {
@@ -65,5 +65,5 @@ export default {
created() { created() {
this.updateStyle(); this.updateStyle();
} }
} };
</script> </script>

View File

@@ -50,326 +50,399 @@
</template> </template>
<script> <script>
import Vue3DraggableResizable from 'vue3-draggable-resizable'; import Vue3DraggableResizable from 'vue3-draggable-resizable';
import circularSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/seats/circular.vue'; import circularSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/seats/circular.vue';
import rectangularSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/seats/rectangular.vue'; import rectangularSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/seats/rectangular.vue';
import trapezoidSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/seats/trapezoid.vue'; import trapezoidSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/seats/trapezoid.vue';
import stagesSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/stages.vue'; import stagesSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/stages.vue';
import standingSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/standing.vue'; import standingSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/standing.vue';
import textFieldSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/textField.vue'; import textFieldSeatplanComponent from '@/components/seatplan/userApp/seatplanComponents/textField.vue';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
import popups from '@/components/notifications/popups.vue'; import popups from '@/components/notifications/popups.vue';
import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css'; import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css';
import sideCartView from '@/components/sideCartView.vue'; import sideCartView from '@/components/sideCartView.vue';
export default { export default {
'name': 'window', 'name': 'window',
components: { components: {
Vue3DraggableResizable, Vue3DraggableResizable,
circularSeatplanComponent, circularSeatplanComponent,
rectangularSeatplanComponent, rectangularSeatplanComponent,
trapezoidSeatplanComponent, trapezoidSeatplanComponent,
stagesSeatplanComponent, stagesSeatplanComponent,
standingSeatplanComponent, standingSeatplanComponent,
textFieldSeatplanComponent, textFieldSeatplanComponent,
notifications, notifications,
popups, popups,
sideCartView, sideCartView,
}, },
data() { data() {
return { return {
draggables: { 1: { 'x': 100, 'y':100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': 1, 'origin': 1, 'shape':'rectangular', 'type': 'seat', 'startingRow': 1, 'seatCountingStartingPoint': 1, 'sector': 'A', 'text': { 'text': 'TestText', 'textSize': 20, 'colour': '#20FFFF' }, 'ticketCount': 1, 'category': 1 } }, draggables: { 1: { 'x': 100, 'y': 100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': 1, 'origin': 1, 'shape': 'rectangular', 'type': 'seat', 'startingRow': 1, 'seatCountingStartingPoint': 1, 'sector': 'A', 'text': { 'text': 'TestText', 'textSize': 20, 'colour': '#20FFFF' }, 'ticketCount': 1, 'category': 1 } },
event: { 'name': 'TestEvent2', 'location': 'TestLocation2', 'eventID': 'test2', 'date': '2023-07-15', 'currency': 'CHF', 'categories': { '1': { 'price': { '1':25, '2':35 }, 'bg': 'black', 'fg': 'white', 'name': 'Category 1' }, '2': { 'price': { '1':15, '2':20 }, 'bg': 'green', 'fg': 'white', 'name': 'Category 2' } }, 'ageGroups': { '1':{ 'id': 1, 'name':'Child', 'age':'0 - 15.99' }, '2':{ 'id': 2, 'name': 'Adult' } }, 'maxTickets': 2 }, event: { 'name': 'TestEvent2', 'location': 'TestLocation2', 'eventID': 'test2', 'date': '2023-07-15', 'currency': 'CHF', 'categories': { '1': { 'price': { '1': 25, '2': 35 }, 'bg': 'black', 'fg': 'white', 'name': 'Category 1' }, '2': { 'price': { '1': 15, '2': 20 }, 'bg': 'green', 'fg': 'white', 'name': 'Category 2' } }, 'ageGroups': { '1': { 'id': 1, 'name': 'Child', 'age': '0 - 15.99' }, '2': { 'id': 2, 'name': 'Adult' } }, 'maxTickets': 2 },
available: { 'redo': false, 'undo': false }, available: { 'redo': false, 'undo': false },
scaleFactor: 1, scaleFactor: 1,
sizePoll: null, sizePoll: null,
prevSize: { 'h': window.innerHeight, 'w': window.innerWidth }, prevSize: { 'h': window.innerHeight, 'w': window.innerWidth },
zoomFactor: 1, zoomFactor: 1,
standardDeviation: { 'currentTop': 0, 'currentLeft': 0 }, standardDeviation: { 'currentTop': 0, 'currentLeft': 0 },
movePos: { 'top': 0, 'left': 0, 'isMoving': false, 'isSet': false }, movePos: { 'top': 0, 'left': 0, 'isMoving': false, 'isSet': false },
generalSettings: { 'namingScheme': 'numeric' }, generalSettings: { 'namingScheme': 'numeric' },
selectedSeat: {}, selectedSeat: {},
cart: {}, cart: {},
unavailableSeats: {}, unavailableSeats: {},
} };
}, },
methods: { methods: {
/* /*
Coords are from top left corner of box. Coords are from top left corner of box.
The below function is executed as the init hook (created hook) The below function is executed as the init hook (created hook)
of vue.js, so whenever this particular page is loaded. of vue.js, so whenever this particular page is loaded.
It loads seat plan data and starts the event listeners It loads seat plan data and starts the event listeners
for keyevents (like +, -, =) for keyevents (like +, -, =)
*/ */
runHook () { runHook () {
let self = this; let self = this;
this.zoomFactor = sessionStorage.getItem( 'zoom' ) ? parseFloat( sessionStorage.getItem( 'zoom' ) ) : 1; this.zoomFactor = sessionStorage.getItem( 'zoom' ) ? parseFloat( sessionStorage.getItem( 'zoom' ) ) : 1;
document.onkeydown = function ( event ) { document.onkeydown = function ( event ) {
if ( event.key === '+' ) { if ( event.key === '+' ) {
self.zoom( 0.2 ); self.zoom( 0.2 );
} else if ( event.key === '-' ) { } else if ( event.key === '-' ) {
self.zoom( -0.2 ); self.zoom( -0.2 );
} else if ( event.key === '=' ) { } else if ( event.key === '=' ) {
self.zoom( 1 ); self.zoom( 1 );
} }
};
this.seatPlanInit();
},
seatPlanInit () {
// Load cart
this.cart = localStorage.getItem( 'cart' ) ? JSON.parse( localStorage.getItem( 'cart' ) ): {};
// Load seatplan from server
let height = $( document ).height() * 0.8;
this.scaleFactor = ( height / 900 ) * this.zoomFactor;
fetch( '/getAPI/getEvent?event=' + sessionStorage.getItem( 'selectedTicket' ) ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.event = json ?? {};
fetch( localStorage.getItem( 'url' ) + '/getAPI/getSeatplan?location=' + this.event.location ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
this.draggables = this.scaleUp( data.data );
this.prepSeatplan( data.seatInfo );
} );
} else if ( res.status === 500 ) {
if ( sessionStorage.getItem( 'seatplan' ) ) {
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan' ) ) );
this.prepSeatplan( {} );
}
}
} );
} );
}
} );
},
prepSeatplan ( seatInfo ) {
// Mark all selected seats + all unavailable seats
let categoryDetails = {};
for ( let category in this.event.categories ) {
categoryDetails[ category ] = {};
for ( let group in this.event.ageGroups ) {
categoryDetails[ category ][ group ] = {};
categoryDetails[ category ][ group ] = { 'displayName': this.event.ageGroups[ group ].name + ( this.event.ageGroups[ group ].age ? ' (' + this.event.ageGroups[ group ].age + ')' : '' ) + ' - ' + this.event.currency + ' ' + this.event.categories[ category ].price[ group ], 'value': group, 'price': this.event.categories[ category ].price[ group ] };
}
}
for ( let element in this.draggables ) {
this.draggables[ element ][ 'data' ] = {
'sector': this.draggables[ element ][ 'sector' ],
'categoryInfo': {
'pricing': categoryDetails[ this.draggables[ element ][ 'category' ] ],
'color': this.event.categories[ this.draggables[ element ][ 'category' ] ][ 'fg' ]
},
'seatInfo': seatInfo,
'seatNumbering': this.draggables[ element ].seatNumberingPosition,
'numberingDirection': this.draggables[ element ].numberingDirection,
}; };
}
this.seatChecks();
// TODO: FUTURE Trim scroll box to about 200px more than seatplan size
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) );
window.addEventListener( 'visibilitychange', ( e ) => {
this.seatPlanInit(); this.seatPlanInit();
}, }, 1 );
seatPlanInit () { },
// Load cart seatChecks () {
this.cart = localStorage.getItem( 'cart' ) ? JSON.parse( localStorage.getItem( 'cart' ) ): {}; let self = this;
let allSeatsAvailable = true;
// Load seatplan from server fetch( localStorage.getItem( 'url' ) + '/getAPI/getReservedSeats?event=' + this.event.eventID ).then( res => {
let height = $( document ).height() * 0.8; if ( res.status === 200 ) {
this.scaleFactor = ( height / 900 ) * this.zoomFactor; let unavailableSeats = {};
fetch( '/getAPI/getEvent?event=' + sessionStorage.getItem( 'selectedTicket' ) ).then( res => { res.json().then( data => {
if ( res.status === 200 ) { for ( let seat in data.reserved ) {
res.json().then( json => { if ( data.reserved[ seat ] ) {
this.event = json ?? {}; if ( !unavailableSeats[ data.reserved[ seat ].component ] ) {
fetch( localStorage.getItem( 'url' ) + '/getAPI/getSeatplan?location=' + this.event.location ).then( res => { unavailableSeats[ data.reserved[ seat ].component ] = {};
if ( res.status === 200 ) {
res.json().then( data => {
this.draggables = this.scaleUp( data.data );
this.prepSeatplan( data.seatInfo );
} );
} else if ( res.status === 500 ) {
if ( sessionStorage.getItem( 'seatplan' ) ) {
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan' ) ) );
this.prepSeatplan( {} );
}
}
} );
} );
}
} );
},
prepSeatplan ( seatInfo ) {
// Mark all selected seats + all unavailable seats
let categoryDetails = {};
for ( let category in this.event.categories ) {
categoryDetails[ category ] = {};
for ( let group in this.event.ageGroups ) {
categoryDetails[ category ][ group ] = {};
categoryDetails[ category ][ group ] = { 'displayName': this.event.ageGroups[ group ].name + ( this.event.ageGroups[ group ].age ? ' (' + this.event.ageGroups[ group ].age + ')' : '' ) + ' - ' + this.event.currency + ' ' + this.event.categories[ category ].price[ group ], 'value': group, 'price': this.event.categories[ category ].price[ group ] };
}
}
for ( let element in this.draggables ) {
this.draggables[ element ][ 'data' ] = {
'sector': this.draggables[ element ][ 'sector' ],
'categoryInfo': {
'pricing': categoryDetails[ this.draggables[ element ][ 'category' ] ],
'color': this.event.categories[ this.draggables[ element ][ 'category' ] ][ 'fg' ]
},
'seatInfo': seatInfo,
'seatNumbering': this.draggables[ element ].seatNumberingPosition,
'numberingDirection': this.draggables[ element ].numberingDirection,
};
}
this.seatChecks();
// TODO: FUTURE Trim scroll box to about 200px more than seatplan size
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) );
window.addEventListener( 'visibilitychange', ( e ) => {
this.seatPlanInit();
}, 1 );
},
seatChecks () {
let self = this;
let allSeatsAvailable = true;
fetch( localStorage.getItem( 'url' ) + '/getAPI/getReservedSeats?event=' + this.event.eventID ).then( res => {
if ( res.status === 200 ) {
let unavailableSeats = {};
res.json().then( data => {
for ( let seat in data.reserved ) {
if ( data.reserved[ seat ] ) {
if ( !unavailableSeats[ data.reserved[ seat ].component ] ) {
unavailableSeats[ data.reserved[ seat ].component ] = {};
}
unavailableSeats[ data.reserved[ seat ].component ][ data.reserved[ seat ].id ] = 'nav';
} }
unavailableSeats[ data.reserved[ seat ].component ][ data.reserved[ seat ].id ] = 'nav';
} }
for ( let seat in data.user ) {
if ( data.user[ seat ] ) {
if ( !unavailableSeats[ data.user[ seat ].component ] ) {
unavailableSeats[ data.user[ seat ].component ] = {};
}
unavailableSeats[ data.user[ seat ].component ][ data.user[ seat ].id ] = 'sel';
}
}
let tickets = {};
if ( this.cart[ this.event.eventID ] ) {
tickets = this.cart[ this.event.eventID ][ 'tickets' ];
}
if ( data.user ) {
for ( let element in tickets ) {
if ( !data.user[ element ] ) {
allSeatsAvailable = false;
if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length > 1 ) {
delete this.cart[ this.event.eventID ][ 'tickets' ][ element ];
} else {
delete this.cart[ this.event.eventID ];
}
}
}
} else {
delete this.cart[ this.event.eventID ];
allSeatsAvailable = false;
}
this.unavailableSeats = unavailableSeats;
if ( !allSeatsAvailable ) {
setTimeout( () => {
self.$refs.popups.openPopup( 'We are sorry to tell you that since the last time the seat plan was refreshed, one or more of the seats you have selected has/have been taken.', {}, 'string' );
}, 500 );
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
}
} );
} else {
console.error( 'unable to load' );
}
} );
},
eventHandler ( e ) {
if ( this.prevSize.h != window.innerHeight || this.prevSize.w != window.innerWidth ) {
this.prevSize = { 'h': window.innerHeight, 'w': window.innerWidth };
this.loadSeatplan();
}
},
handleScroll ( e ) {
e.preventDefault();
if ( e.deltaY > 0 ) {
this.zoom( 0.2 );
} else {
this.zoom( -0.2 );
}
},
setOffset( e ) {
this.standardDeviation.currentLeft = e.clientX;
this.standardDeviation.currentTop = e.clientY;
},
handleDrag ( e ) {
if ( e.buttons === 1 ) {
let parent = document.getElementById( 'parent' );
if ( !this.movePos.isSet ) {
this.movePos.left = parent.scrollWidth - parent.scrollLeft;
this.movePos.top = parent.scrollHeight - parent.scrollTop;
this.movePos.isSet = true;
}
this.movePos.isMoving = true;
e.preventDefault();
let valueTop = parent.scrollHeight - ( e.clientY - this.standardDeviation.currentTop + this.movePos.top );
let valueLeft = parent.scrollWidth - ( e.clientX - this.standardDeviation.currentLeft + this.movePos.left );
parent.scrollTop = valueTop > 0 ? valueTop : 0;
parent.scrollLeft = valueLeft > 0 ? valueLeft : 0;
} else {
if ( this.movePos.isMoving ) {
let parent = document.getElementById( 'parent' );
this.movePos.left = parent.scrollWidth - parent.scrollLeft;
this.movePos.top = parent.scrollHeight - parent.scrollTop;
this.movePos.isMoving = false;
}
};
},
scaleDown ( valueArray ) {
const allowedAttributes = [ 'w', 'h', 'x', 'y' ]
let returnArray = {};
for ( let entry in valueArray ) {
returnArray[ entry ] = {};
for ( let attributes in valueArray[ entry ] ) {
if ( allowedAttributes.includes( attributes ) ) {
returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] / this.scaleFactor ) * 1000 ) / 1000;
} else {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
} }
for ( let seat in data.user ) {
if ( data.user[ seat ] ) {
if ( !unavailableSeats[ data.user[ seat ].component ] ) {
unavailableSeats[ data.user[ seat ].component ] = {};
}
unavailableSeats[ data.user[ seat ].component ][ data.user[ seat ].id ] = 'sel';
}
}
let tickets = {};
if ( this.cart[ this.event.eventID ] ) {
tickets = this.cart[ this.event.eventID ][ 'tickets' ];
}
if ( data.user ) {
for ( let element in tickets ) {
if ( !data.user[ element ] ) {
allSeatsAvailable = false;
if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length > 1 ) {
delete this.cart[ this.event.eventID ][ 'tickets' ][ element ];
} else {
delete this.cart[ this.event.eventID ];
}
}
}
} else {
delete this.cart[ this.event.eventID ];
allSeatsAvailable = false;
}
this.unavailableSeats = unavailableSeats;
if ( !allSeatsAvailable ) {
setTimeout( () => {
self.$refs.popups.openPopup( 'We are sorry to tell you that since the last time the seat plan was refreshed, one or more of the seats you have selected has/have been taken.', {}, 'string' );
}, 500 );
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
}
} );
} else {
console.error( 'unable to load' );
}
} );
},
eventHandler ( e ) {
if ( this.prevSize.h != window.innerHeight || this.prevSize.w != window.innerWidth ) {
this.prevSize = { 'h': window.innerHeight, 'w': window.innerWidth };
this.loadSeatplan();
}
},
handleScroll ( e ) {
e.preventDefault();
if ( e.deltaY > 0 ) {
this.zoom( 0.2 );
} else {
this.zoom( -0.2 );
}
},
setOffset( e ) {
this.standardDeviation.currentLeft = e.clientX;
this.standardDeviation.currentTop = e.clientY;
},
handleDrag ( e ) {
if ( e.buttons === 1 ) {
let parent = document.getElementById( 'parent' );
if ( !this.movePos.isSet ) {
this.movePos.left = parent.scrollWidth - parent.scrollLeft;
this.movePos.top = parent.scrollHeight - parent.scrollTop;
this.movePos.isSet = true;
}
this.movePos.isMoving = true;
e.preventDefault();
let valueTop = parent.scrollHeight - ( e.clientY - this.standardDeviation.currentTop + this.movePos.top );
let valueLeft = parent.scrollWidth - ( e.clientX - this.standardDeviation.currentLeft + this.movePos.left );
parent.scrollTop = valueTop > 0 ? valueTop : 0;
parent.scrollLeft = valueLeft > 0 ? valueLeft : 0;
} else {
if ( this.movePos.isMoving ) {
let parent = document.getElementById( 'parent' );
this.movePos.left = parent.scrollWidth - parent.scrollLeft;
this.movePos.top = parent.scrollHeight - parent.scrollTop;
this.movePos.isMoving = false;
}
}
},
scaleDown ( valueArray ) {
const allowedAttributes = [ 'w', 'h', 'x', 'y' ];
let returnArray = {};
for ( let entry in valueArray ) {
returnArray[ entry ] = {};
for ( let attributes in valueArray[ entry ] ) {
if ( allowedAttributes.includes( attributes ) ) {
returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] / this.scaleFactor ) * 1000 ) / 1000;
} else {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
} }
} }
return returnArray; }
}, return returnArray;
loadSeatplan () { },
/* loadSeatplan () {
/*
Calculate scale factor (this adds support for differently sized screens) Calculate scale factor (this adds support for differently sized screens)
900px is the "default" height 900px is the "default" height
*/ */
let height = $( document ).height() * 0.8; let height = $( document ).height() * 0.8;
this.scaleFactor = ( height / 900 ) * this.zoomFactor; this.scaleFactor = ( height / 900 ) * this.zoomFactor;
/* /*
Load seatplan Load seatplan
*/ */
if ( sessionStorage.getItem( 'seatplan' ) ) { if ( sessionStorage.getItem( 'seatplan' ) ) {
this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan' ) ) ); this.draggables = this.scaleUp( JSON.parse( sessionStorage.getItem( 'seatplan' ) ) );
} }
}, },
scaleUp ( valueArray ) { scaleUp ( valueArray ) {
const allowedAttributes = [ 'w', 'h', 'x', 'y' ]; const allowedAttributes = [ 'w', 'h', 'x', 'y' ];
let returnArray = {}; let returnArray = {};
for ( let entry in valueArray ) { for ( let entry in valueArray ) {
returnArray[ entry ] = {}; returnArray[ entry ] = {};
for ( let attributes in valueArray[ entry ] ) { for ( let attributes in valueArray[ entry ] ) {
if ( allowedAttributes.includes( attributes ) ) { if ( allowedAttributes.includes( attributes ) ) {
returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] * this.scaleFactor ) * 1000 ) / 1000; returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] * this.scaleFactor ) * 1000 ) / 1000;
} else { } else {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ]; returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
}
} }
} }
return returnArray; }
}, return returnArray;
zoom ( scale ) { },
if ( scale == 1 ) { zoom ( scale ) {
this.zoomFactor = 1; if ( scale == 1 ) {
sessionStorage.setItem( 'zoom', this.zoomFactor ); this.zoomFactor = 1;
this.loadSeatplan(); sessionStorage.setItem( 'zoom', this.zoomFactor );
} else { this.loadSeatplan();
if ( ( this.zoomFactor < 0.3 && scale < 0 ) || ( this.zoomFactor > 2.9 && scale > 0 ) ) { } else {
if ( ( this.zoomFactor < 0.3 && scale < 0 ) || ( this.zoomFactor > 2.9 && scale > 0 ) ) {
} else {
this.zoomFactor += scale;
}
sessionStorage.setItem( 'zoom', this.zoomFactor );
this.loadSeatplan();
}
},
seatSelected ( seat ) {
this.selectedSeat = seat;
if ( Object.keys( seat.option ).length > 1 ) {
this.$refs.popups.openPopup( 'Please choose a ticket option', seat.option, 'selection', 'adult' );
} else { } else {
this.reserveTicket( { 'status': 'ok', 'data': Object.keys( seat.option )[ 0 ][ 'value' ], 'id': this.selectedSeat.componentID } ); this.zoomFactor += scale;
} }
}, sessionStorage.setItem( 'zoom', this.zoomFactor );
cartHandling ( operation, data ) { this.loadSeatplan();
if ( operation === 'select' ) { }
if ( this.cart[ this.event.eventID ] ) { },
this.cart[ this.event.eventID ][ 'tickets' ][ this.selectedSeat.id ] = { 'displayName': this.selectedSeat.displayName, 'price': this.selectedSeat.option[ data ].price, 'id': this.selectedSeat.id, 'option': data, 'comp': this.selectedSeat.componentID }; seatSelected ( seat ) {
} else { this.selectedSeat = seat;
this.cart[ this.event.eventID ] = { 'displayName': this.event.name, 'tickets': {}, 'eventID': this.event.eventID }; if ( Object.keys( seat.option ).length > 1 ) {
this.cart[ this.event.eventID ][ 'tickets' ][ this.selectedSeat.id ] = { 'displayName': this.selectedSeat.displayName, 'price': this.selectedSeat.option[ data ].price, 'id': this.selectedSeat.id, 'option': data, 'comp': this.selectedSeat.componentID }; this.$refs.popups.openPopup( 'Please choose a ticket option', seat.option, 'selection', 'adult' );
} else {
this.reserveTicket( { 'status': 'ok', 'data': Object.keys( seat.option )[ 0 ][ 'value' ], 'id': this.selectedSeat.componentID } );
}
},
cartHandling ( operation, data ) {
if ( operation === 'select' ) {
if ( this.cart[ this.event.eventID ] ) {
this.cart[ this.event.eventID ][ 'tickets' ][ this.selectedSeat.id ] = { 'displayName': this.selectedSeat.displayName, 'price': this.selectedSeat.option[ data ].price, 'id': this.selectedSeat.id, 'option': data, 'comp': this.selectedSeat.componentID };
} else {
this.cart[ this.event.eventID ] = { 'displayName': this.event.name, 'tickets': {}, 'eventID': this.event.eventID };
this.cart[ this.event.eventID ][ 'tickets' ][ this.selectedSeat.id ] = { 'displayName': this.selectedSeat.displayName, 'price': this.selectedSeat.option[ data ].price, 'id': this.selectedSeat.id, 'option': data, 'comp': this.selectedSeat.componentID };
}
} else if ( operation === 'deselect' ) {
if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length > 1 ) {
delete this.cart[ this.event.eventID ][ 'tickets' ][ this.selectedSeat.id ];
} else {
delete this.cart[ this.event.eventID ];
}
}
this.$refs.cart.calculateTotal();
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
},
reserveTicket ( option ) {
if ( option.status == 'ok' && option.data ) {
// Make call to server to reserve ticket to have server also keep track of reserved tickets
const options = {
method: 'post',
body: JSON.stringify( { 'id': this.selectedSeat[ 'id' ], 'component': this.selectedSeat[ 'componentID' ], 'ticketOption': option.data, 'eventID': this.event.eventID, 'category': this.draggables[ this.selectedSeat[ 'componentID' ] ].category, 'name': this.selectedSeat.displayName } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
} }
} else if ( operation === 'deselect' ) { };
if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length > 1 ) { fetch( localStorage.getItem( 'url' ) + '/API/reserveTicket', options ).then( res => {
delete this.cart[ this.event.eventID ][ 'tickets' ][ this.selectedSeat.id ]; if ( res.status === 200 ) {
this.$refs[ 'component' + this.selectedSeat.componentID ][ 0 ].validateSeatSelection( this.selectedSeat, option.data );
this.cartHandling( 'select', option.data );
} else if ( res.status === 409 ) {
setTimeout( () => {
this.$refs.popups.openPopup( 'Unfortunately, the seat you just tried to select was reserved by somebody else since the last time the seat plan was refreshed. Please select another one. We are sorry for the inconvenience.', {}, 'string' );
}, 300 );
} else if ( res.status === 418 ) {
setTimeout( () => {
this.$refs.popups.openPopup( 'We are sorry, but you have already selected the maximum amount of tickets you can buy at once.', {}, 'string' );
}, 300 );
}
} );
}
},
seatDeselected ( seat ) {
this.selectedSeat = seat;
this.cartHandling( 'deselect' );
// Make call to server to deselect ticket
const options = {
method: 'post',
body: JSON.stringify( { 'id': seat[ 'id' ], 'eventID': this.event.eventID, 'component': seat.componentID } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/API/deselectTicket', options );
},
standing ( id ) {
const d = this.draggables[ id ];
const evG = this.event.ageGroups;
let count = {};
if ( this.cart[ this.event.eventID ] ) {
for ( let ageGroup in evG ) {
if ( this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + id + '_' + ageGroup ] ) {
count[ ageGroup ] = this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + id + '_' + ageGroup ].count;
} else { } else {
delete this.cart[ this.event.eventID ]; count[ ageGroup ] = 0;
} }
} }
this.$refs.cart.calculateTotal(); } else {
localStorage.setItem( 'cart', JSON.stringify( this.cart ) ); for ( let ageGroup in evG ) {
}, count[ ageGroup ] = 0;
reserveTicket ( option ) { }
if ( option.status == 'ok' && option.data ) { }
// Make call to server to reserve ticket to have server also keep track of reserved tickets this.$refs.popups.openPopup( 'Select tickets', {
'id': id,
'max': d.ticketCount,
'name': 'Sector ' + d.sector + ' Ticket ' + id,
'ageGroups': this.event.ageGroups,
'currency': this.event.currency,
'price': this.event.categories[ d.category ],
'count': count
}, 'tickets' );
},
standingTicketHandling ( data ) {
if ( !this.cart[ this.event.eventID ] ) {
this.cart[ this.event.eventID ] = { 'displayName': this.event.name, 'tickets': {}, 'eventID': this.event.eventID };
}
for ( let group in data.data ) {
if ( data.data[ group ] > 0 ) {
const options = { const options = {
method: 'post', method: 'post',
body: JSON.stringify( { 'id': this.selectedSeat[ 'id' ], 'component': this.selectedSeat[ 'componentID' ], 'ticketOption': option.data, 'eventID': this.event.eventID, 'category': this.draggables[ this.selectedSeat[ 'componentID' ] ].category, 'name': this.selectedSeat.displayName } ), body: JSON.stringify( { 'id': 'ticket' + data.component + '_' + group, 'component': data.component, 'ticketOption': group, 'eventID': this.event.eventID, 'count': data.data[ group ], 'category': this.draggables[ data.component ].category, 'name': 'Ticket ' + data.component + ' (' + this.event.ageGroups[ group ].name + ')' } ),
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'charset': 'utf-8' 'charset': 'utf-8'
@@ -377,132 +450,59 @@
}; };
fetch( localStorage.getItem( 'url' ) + '/API/reserveTicket', options ).then( res => { fetch( localStorage.getItem( 'url' ) + '/API/reserveTicket', options ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
this.$refs[ 'component' + this.selectedSeat.componentID ][ 0 ].validateSeatSelection( this.selectedSeat, option.data ); this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + data.component + '_' + group ] = { 'displayName': 'Ticket ' + data.component + ' (' + this.event.ageGroups[ group ].name + ')', 'price': this.event.categories[ this.draggables[ data.component ].category ].price[ group ], 'id': 'ticket' + data.component + '_' + group, 'count': data.data[ group ], 'comp': data.component };
this.cartHandling( 'select', option.data );
} else if ( res.status === 409 ) { } else if ( res.status === 409 ) {
res.json().then( dat => {
this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + data.component + '_' + group ] = { 'displayName': 'Ticket ' + data.component + ' (' + this.event.ageGroups[ group ].name + ')', 'price': this.event.categories[ this.draggables[ data.component ].category ].price[ group ], 'id': 'ticket' + data.component + '_' + group, 'count': dat.count, 'comp': data.component };
} );
setTimeout( () => { setTimeout( () => {
this.$refs.popups.openPopup( 'Unfortunately, the seat you just tried to select was reserved by somebody else since the last time the seat plan was refreshed. Please select another one. We are sorry for the inconvenience.', {}, 'string' ); this.$refs.popups.openPopup( 'Unfortunately, you have selected more tickets than were still available. The maximum amount of tickets that are available have been selected for you automatically. We are sorry for the inconvenience.', {}, 'string' );
}, 300 ); }, 300 );
} else if ( res.status === 418 ) { } else if ( res.status === 418 ) {
setTimeout( () => { setTimeout( () => {
this.$refs.popups.openPopup( 'We are sorry, but you have already selected the maximum amount of tickets you can buy at once.', {}, 'string' ); this.$refs.popups.openPopup( 'We are sorry, but you have already selected the maximum amount of tickets you can buy at once.', {}, 'string' );
}, 300 ); }, 300 );
} }
} ); if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length < 1 ) {
} delete this.cart[ this.event.eventID ];
},
seatDeselected ( seat ) {
this.selectedSeat = seat;
this.cartHandling( 'deselect' );
// Make call to server to deselect ticket
const options = {
method: 'post',
body: JSON.stringify( { 'id': seat[ 'id' ], 'eventID': this.event.eventID, 'component': seat.componentID } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/API/deselectTicket', options );
},
standing ( id ) {
const d = this.draggables[ id ];
const evG = this.event.ageGroups;
let count = {};
if ( this.cart[ this.event.eventID ] ) {
for ( let ageGroup in evG ) {
if ( this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + id + '_' + ageGroup ] ) {
count[ ageGroup ] = this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + id + '_' + ageGroup ].count;
} else {
count[ ageGroup ] = 0;
} }
}
this.$refs.cart.calculateTotal();
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
} );
} else { } else {
for ( let ageGroup in evG ) { if ( this.cart[ this.event.eventID ] ) {
count[ ageGroup ] = 0; if ( this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + data.component + '_' + group ] ) {
} delete this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + data.component + '_' + group ];
} if ( this.cart[ this.event.eventID ] ) {
this.$refs.popups.openPopup( 'Select tickets', { if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length < 1 ) {
'id': id, delete this.cart[ this.event.eventID ];
'max': d.ticketCount,
'name': 'Sector ' + d.sector + ' Ticket ' + id,
'ageGroups': this.event.ageGroups,
'currency': this.event.currency,
'price': this.event.categories[ d.category ],
'count': count
}, 'tickets' );
},
standingTicketHandling ( data ) {
if ( !this.cart[ this.event.eventID ] ) {
this.cart[ this.event.eventID ] = { 'displayName': this.event.name, 'tickets': {}, 'eventID': this.event.eventID };
}
for ( let group in data.data ) {
if ( data.data[ group ] > 0 ) {
const options = {
method: 'post',
body: JSON.stringify( { 'id': 'ticket' + data.component + '_' + group, 'component': data.component, 'ticketOption': group, 'eventID': this.event.eventID, 'count': data.data[ group ], 'category': this.draggables[ data.component ].category, 'name': 'Ticket ' + data.component + ' (' + this.event.ageGroups[ group ].name + ')' } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/API/reserveTicket', options ).then( res => {
if ( res.status === 200 ) {
this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + data.component + '_' + group ] = { 'displayName': 'Ticket ' + data.component + ' (' + this.event.ageGroups[ group ].name + ')', 'price': this.event.categories[ this.draggables[ data.component ].category ].price[ group ], 'id': 'ticket' + data.component + '_' + group, 'count': data.data[ group ], 'comp': data.component };
} else if ( res.status === 409 ) {
res.json().then( dat => {
this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + data.component + '_' + group ] = { 'displayName': 'Ticket ' + data.component + ' (' + this.event.ageGroups[ group ].name + ')', 'price': this.event.categories[ this.draggables[ data.component ].category ].price[ group ], 'id': 'ticket' + data.component + '_' + group, 'count': dat.count, 'comp': data.component };
} );
setTimeout( () => {
this.$refs.popups.openPopup( 'Unfortunately, you have selected more tickets than were still available. The maximum amount of tickets that are available have been selected for you automatically. We are sorry for the inconvenience.', {}, 'string' );
}, 300 );
} else if ( res.status === 418 ) {
setTimeout( () => {
this.$refs.popups.openPopup( 'We are sorry, but you have already selected the maximum amount of tickets you can buy at once.', {}, 'string' );
}, 300 );
}
if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length < 1 ) {
delete this.cart[ this.event.eventID ];
}
this.$refs.cart.calculateTotal();
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
} );
} else {
if ( this.cart[ this.event.eventID ] ) {
if ( this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + data.component + '_' + group ] ) {
delete this.cart[ this.event.eventID ][ 'tickets' ][ 'ticket' + data.component + '_' + group ];
if ( this.cart[ this.event.eventID ] ) {
if ( Object.keys( this.cart[ this.event.eventID ][ 'tickets' ] ).length < 1 ) {
delete this.cart[ this.event.eventID ];
}
} }
const options = {
method: 'post',
body: JSON.stringify( { 'id': 'ticket' + data.component + '_' + group, 'eventID': this.event.eventID, 'component': data.component } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/API/deselectTicket', options );
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
} }
const options = {
method: 'post',
body: JSON.stringify( { 'id': 'ticket' + data.component + '_' + group, 'eventID': this.event.eventID, 'component': data.component } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/API/deselectTicket', options );
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
} }
} }
} }
} }
}, }
created () { },
this.runHook(); created () {
this.sizePoll = setInterval( this.eventHandler, 250 ); this.runHook();
}, this.sizePoll = setInterval( this.eventHandler, 250 );
unmounted() { },
clearInterval( this.sizePoll ); unmounted() {
}, clearInterval( this.sizePoll );
} },
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -24,7 +24,7 @@ export default {
data() { data() {
return { return {
options: {}, options: {},
} };
}, },
methods: { methods: {
openRightClickMenu( event, options ) { openRightClickMenu( event, options ) {
@@ -45,7 +45,7 @@ export default {
this.$emit( 'command', command ); this.$emit( 'command', command );
} }
} }
} };
</script> </script>
<style scoped> <style scoped>

View File

@@ -63,29 +63,29 @@
</template> </template>
<script> <script>
export default { export default {
data () { data () {
return { return {
showsPW: false, showsPW: false,
}; };
},
props: {
settings: Object,
},
methods: {
togglePasswordVisibility () {
this.showsPW = !this.showsPW;
}, },
props: { showInfo ( box ) {
settings: Object, $( '#' + box ).stop();
$( '#' + box ).fadeIn( 300 );
}, },
methods: { hideInfo ( box ) {
togglePasswordVisibility () { $( '#' + box ).stop();
this.showsPW = !this.showsPW; $( '#' + box ).fadeOut( 300 );
},
showInfo ( box ) {
$( '#' + box ).stop();
$( '#' + box ).fadeIn( 300 );
},
hideInfo ( box ) {
$( '#' + box ).stop();
$( '#' + box ).fadeOut( 300 );
}
} }
}; }
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -74,7 +74,7 @@ export default {
data() { data() {
return { return {
total: 0, total: 0,
} };
}, },
methods: { methods: {
calculateTotal () { calculateTotal () {
@@ -89,7 +89,7 @@ export default {
created() { created() {
this.calculateTotal(); this.calculateTotal();
} }
} };
</script> </script>
<style scoped> <style scoped>

View File

@@ -90,4 +90,4 @@ export default {
} }
}, },
] ]
} };

View File

@@ -7,7 +7,7 @@
* *
*/ */
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router';
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from '@/stores/userStore';
import { useBackendStore } from '@/stores/backendStore'; import { useBackendStore } from '@/stores/backendStore';
import adminRoutes from '@/router/adminRoutes'; import adminRoutes from '@/router/adminRoutes';

View File

@@ -170,4 +170,4 @@ export default [
transition: 'scale', transition: 'scale',
} }
}, },
] ];

View File

@@ -7,9 +7,9 @@
* *
*/ */
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
export const useBackendStore = defineStore ( 'backend', { export const useBackendStore = defineStore( 'backend', {
state: () => ( { 'guestPurchase': false, 'guestPurchaseAllowed': false } ), state: () => ( { 'guestPurchase': false, 'guestPurchaseAllowed': false } ),
getters: { getters: {
getIsGuestPurchase: ( state ) => state.guestPurchase, getIsGuestPurchase: ( state ) => state.guestPurchase,

View File

@@ -7,9 +7,9 @@
* *
*/ */
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
export const useUserStore = defineStore ( 'user', { export const useUserStore = defineStore( 'user', {
state: () => ( { 'isUserAuth': false, 'isAdminAuth': false, 'userData': {}, 'isTwoFACompliantUser': false, 'isTwoFACompliantAdmin': false, 'pageName': 'libreevent' } ), state: () => ( { 'isUserAuth': false, 'isAdminAuth': false, 'userData': {}, 'isTwoFACompliantUser': false, 'isTwoFACompliantAdmin': false, 'pageName': 'libreevent' } ),
getters: { getters: {
getUserAuthenticated: ( state ) => state.isUserAuth, getUserAuthenticated: ( state ) => state.isUserAuth,

View File

@@ -14,16 +14,16 @@
</template> </template>
<script> <script>
export default { export default {
data () { data () {
return { return {
formData: {} formData: {}
} };
}, },
methods: { methods: {
setup () { setup () {
} }
}, },
}; };
</script> </script>

View File

@@ -24,63 +24,63 @@
</template> </template>
<script> <script>
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from '@/stores/userStore';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
export default { export default {
data () { data () {
return { return {
formData: {} formData: {}
};
},
components: {
notifications,
},
computed: {
...mapStores( useUserStore )
},
methods: {
login () {
if ( this.formData.mail ) {
if ( this.formData.password ) {
let progress = this.$refs.notification.createNotification( 'Logging you in', 20, 'progress', 'normal' );
let fetchOptions = {
method: 'post',
body: JSON.stringify( this.formData ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/auth', fetchOptions ).then( res => {
res.json().then( json => {
if ( json.status === 'ok' ) {
this.userStore.setAdminAuth( true );
this.$router.push( '/admin' );
sessionStorage.removeItem( 'redirect' );
} else if ( json.status === '2fa' ) {
this.userStore.setAdmin2fa( true );
this.$router.push( '/admin/twoFactors' );
} else if ( json.status === '2fa+' ) {
this.userStore.setAdmin2fa( true );
sessionStorage.setItem( '2faCode', json.code );
this.$router.push( '/admin/twoFactors' );
} else {
this.$refs.notification.cancelNotification( progress );
this.$refs.notification.createNotification( 'The credentials you provided do not match our records.', 5, 'error', 'normal' );
}
} );
} );
} else {
this.$refs.notification.createNotification( 'A password is required to log in', 5, 'error', 'normal' );
}
} else {
this.$refs.notification.createNotification( 'An email address is required to log in', 5, 'error', 'normal' );
} }
}, },
components: { },
notifications, };
},
computed: {
...mapStores( useUserStore )
},
methods: {
login () {
if ( this.formData.mail ) {
if ( this.formData.password ) {
let progress = this.$refs.notification.createNotification( 'Logging you in', 20, 'progress', 'normal' );
let fetchOptions = {
method: 'post',
body: JSON.stringify( this.formData ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/auth', fetchOptions ).then( res => {
res.json().then( json => {
if ( json.status === 'ok' ) {
this.userStore.setAdminAuth( true );
this.$router.push( '/admin' );
sessionStorage.removeItem( 'redirect' );
} else if ( json.status === '2fa' ) {
this.userStore.setAdmin2fa( true );
this.$router.push( '/admin/twoFactors' );
} else if ( json.status === '2fa+' ) {
this.userStore.setAdmin2fa( true );
sessionStorage.setItem( '2faCode', json.code );
this.$router.push( '/admin/twoFactors' );
} else {
this.$refs.notification.cancelNotification( progress );
this.$refs.notification.createNotification( 'The credentials you provided do not match our records.', 5, 'error', 'normal' );
}
} );
} );
} else {
this.$refs.notification.createNotification( 'A password is required to log in', 5, 'error', 'normal' );
}
} else {
this.$refs.notification.createNotification( 'An email address is required to log in', 5, 'error', 'normal' );
}
},
},
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -210,45 +210,45 @@
</style> </style>
<script> <script>
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from '@/stores/userStore';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
export default { export default {
data () { data () {
return { return {
formData: {} formData: {}
};
},
computed: {
...mapStores( useUserStore )
},
methods: {
logout () {
if ( confirm( 'Do you really want to log out?' ) ) {
fetch( '/admin/logout' ).then( _ => {
this.userStore.setAdminAuth( false );
this.$router.push( '/admin/login' );
} );
} }
}, },
computed: { navMenu ( action ) {
...mapStores( useUserStore ) if ( screen.width < 1300 ) {
}, if ( action === 'toggle' ) {
methods: { $( '.side-nav' ).toggle( 300 );
logout () { $( '.hideNav' ).toggleClass( 'navHidden' );
if ( confirm( 'Do you really want to log out?' ) ) { $( '.backdrop' ).toggle( 300 );
fetch( '/admin/logout' ).then( _ => { } else if ( action === 'show' ) {
this.userStore.setAdminAuth( false ); $( '.backdrop' ).show( 300 );
this.$router.push( '/admin/login' ); $( '.side-nav' ).show( 300 );
} ); $( '.hideNav' ).removeClass( 'navHidden' );
} } else {
}, $( '.side-nav' ).hide( 300 );
navMenu ( action ) { $( '.backdrop' ).hide( 300 );
if ( screen.width < 1300 ) { $( '.hideNav' ).addClass( 'navHidden' );
if ( action === 'toggle' ) {
$( '.side-nav' ).toggle( 300 );
$( '.hideNav' ).toggleClass( 'navHidden' );
$( '.backdrop' ).toggle( 300 );
} else if ( action === 'show' ) {
$( '.backdrop' ).show( 300 );
$( '.side-nav' ).show( 300 );
$( '.hideNav' ).removeClass( 'navHidden' );
} else {
$( '.side-nav' ).hide( 300 );
$( '.backdrop' ).hide( 300 );
$( '.hideNav' ).addClass( 'navHidden' );
}
} }
} }
} }
}; }
};
</script> </script>

View File

@@ -96,122 +96,122 @@
</style> </style>
<script> <script>
import popups from '@/components/notifications/popups.vue'; import popups from '@/components/notifications/popups.vue';
import rightClickMenu from '@/components/settings/rightClickMenu.vue'; import rightClickMenu from '@/components/settings/rightClickMenu.vue';
export default { export default {
name: 'OrderView', name: 'OrderView',
components: { components: {
popups, popups,
rightClickMenu, rightClickMenu,
},
data () {
return {
events: { 'test': { 'name': 'TestEvent', 'description': 'This is a description for the TestEvent to test multiline support and proper positioning of the Fields', 'freeSeats': 2, 'maxSeats': 2, 'date': '2023-08-15', 'startingPrice': 15, 'location': 'TestLocation', 'eventID': 'test', 'currency': 'CHF', 'logo': new URL( '/assets/logo.png', import.meta.url ).href } },
currentDate: new Date().getTime(),
eventList: { 'upcoming': { 'name': 'Upcoming', 'content': {} }, 'past': { 'name': 'Past', 'content': {} }, 'drafts': { 'name': 'Drafts', 'content': {} } },
currentlyOpenMenu: '',
};
},
created() {
this.loadData();
},
methods: {
loadData () {
fetch( '/admin/getAPI/getAllEvents' ).then( res => {
res.json().then( dat => {
this.events = dat[ 'live' ] ?? {};
this.eventList.drafts[ 'content' ] = dat[ 'drafts' ] ?? {};
let sortable = [];
for ( let event in this.events ) {
if ( this.events[ event ][ 'description' ].length > 200 ) {
this.events[ event ][ 'shortDescription' ] = this.events[ event ][ 'description' ].slice( 0, 200 ) + '...';
} else {
this.events[ event ][ 'shortDescription' ] = this.events[ event ][ 'description' ];
}
sortable.push( [ this.events[ event ][ 'eventID' ], new Date( this.events[ event ][ 'date' ] ).getTime() ] );
}
sortable.sort( function( a, b ) {
return a[ 1 ] - b[ 1 ];
} );
for ( let element in sortable ) {
if ( this.eventList.drafts[ 'content' ][ sortable[ element ][ 0 ] ] ) {
delete this.eventList.drafts[ 'content' ][ sortable[ element ][ 0 ] ];
}
if ( sortable[ element ][ 1 ] > this.currentDate ) {
this.eventList.upcoming.content[ sortable[ element ][ 0 ] ] = this.events[ sortable[ element ][ 0 ] ];
} else {
this.eventList.past.content[ sortable[ element ][ 0 ] ] = this.events[ sortable[ element ][ 0 ] ];
}
}
} );
} );
}, },
data () { openRightClickMenu( id, event ) {
return { this.$refs.rclk.openRightClickMenu( event, { 'edit': { 'command': 'editEvent', 'symbol': 'edit', 'display': 'Edit event' }, 'delete': { 'command': 'deleteEvent', 'symbol': 'delete', 'display': 'Delete event' } } );
events: { 'test':{ 'name': 'TestEvent', 'description': 'This is a description for the TestEvent to test multiline support and proper positioning of the Fields', 'freeSeats': 2, 'maxSeats': 2, 'date':'2023-08-15', 'startingPrice':15, 'location': 'TestLocation', 'eventID': 'test', 'currency': 'CHF', 'logo': new URL( '/assets/logo.png', import.meta.url ).href } }, this.currentlyOpenMenu = id;
currentDate: new Date().getTime(), },
eventList: { 'upcoming': { 'name': 'Upcoming', 'content': {} }, 'past': { 'name': 'Past', 'content': {} }, 'drafts': { 'name': 'Drafts', 'content': {} } }, executeCommand( command ) {
currentlyOpenMenu: '', if ( command === 'editEvent' ) {
sessionStorage.setItem( 'selectedTicket', this.currentlyOpenMenu );
this.$router.push( '/admin/events/view' );
} else if ( command === 'deleteEvent' ) {
this.$refs.popup.openPopup( 'Do you really want to delete the event ' + this.currentlyOpenMenu + '?', {}, 'confirm' );
this.currentPopup = 'delete';
} }
}, },
created() { addEvent () {
this.loadData(); this.currentPopup = 'add';
this.$refs.popup.openPopup( 'Please give the new event a name for internal use', { 'disallowedCharacters': [ '_', '-' ] }, 'text' );
}, },
methods: { setActiveTicket ( id ) {
loadData () { sessionStorage.setItem( 'selectedTicket', id );
fetch( '/admin/getAPI/getAllEvents' ).then( res => { },
res.json().then( dat => { handleData ( data ) {
this.events = dat[ 'live' ] ?? {}; if ( this.currentPopup === 'add' ) {
this.eventList.drafts[ 'content' ] = dat[ 'drafts' ] ?? {}; if ( data.status === 'ok' ) {
let sortable = []; const options = {
for ( let event in this.events ) { method: 'post',
if ( this.events[ event ][ 'description' ].length > 200 ) { body: JSON.stringify( { 'event': data.data } ),
this.events[ event ][ 'shortDescription' ] = this.events[ event ][ 'description' ].slice( 0, 200 ) + '...'; headers: {
} else { 'Content-Type': 'application/json',
this.events[ event ][ 'shortDescription' ] = this.events[ event ][ 'description' ]; 'charset': 'utf-8'
}
sortable.push( [ this.events[ event ][ 'eventID' ], new Date( this.events[ event ][ 'date' ] ).getTime() ] );
} }
sortable.sort( function( a, b ) { };
return a[ 1 ] - b[ 1 ]; fetch( localStorage.getItem( 'url' ) + '/admin/api/createEvent', options ).then( res => {
} ); if ( res.status === 200 ) {
res.text().then( () => {
for ( let element in sortable ) { this.currentlyOpenMenu = '';
if ( this.eventList.drafts[ 'content' ][ sortable[ element ][ 0 ] ] ) { this.loadData();
delete this.eventList.drafts[ 'content' ][ sortable[ element ][ 0 ] ]; } );
} } else if ( res.status === 409 ) {
if ( sortable[ element ][ 1 ] > this.currentDate ) { this.$refs.popup.openPopup( 'This event does already exist. Please choose a different identifier!', {}, 'string' );
this.eventList.upcoming.content[ sortable[ element ][ 0 ] ] = this.events[ sortable[ element ][ 0 ] ];
} else {
this.eventList.past.content[ sortable[ element ][ 0 ] ] = this.events[ sortable[ element ][ 0 ] ];
}
} }
} ); } );
} );
},
openRightClickMenu( id, event ) {
this.$refs.rclk.openRightClickMenu( event, { 'edit': { 'command': 'editEvent', 'symbol': 'edit', 'display': 'Edit event' }, 'delete': { 'command': 'deleteEvent', 'symbol': 'delete', 'display': 'Delete event' } } )
this.currentlyOpenMenu = id;
},
executeCommand( command ) {
if ( command === 'editEvent' ) {
sessionStorage.setItem( 'selectedTicket', this.currentlyOpenMenu );
this.$router.push( '/admin/events/view' );
} else if ( command === 'deleteEvent' ) {
this.$refs.popup.openPopup( 'Do you really want to delete the event ' + this.currentlyOpenMenu + '?', {}, 'confirm' );
this.currentPopup = 'delete';
} }
}, } else if ( this.currentPopup === 'delete' ) {
addEvent () { console.log( data );
this.currentPopup = 'add'; if ( data.status === 'ok' ) {
this.$refs.popup.openPopup( 'Please give the new event a name for internal use', { 'disallowedCharacters': [ '_', '-' ] }, 'text' ); const options = {
}, method: 'post',
setActiveTicket ( id ) { body: JSON.stringify( { 'event': this.currentlyOpenMenu } ),
sessionStorage.setItem( 'selectedTicket', id ); headers: {
}, 'Content-Type': 'application/json',
handleData ( data ) { 'charset': 'utf-8'
if ( this.currentPopup === 'add' ) { }
if ( data.status === 'ok' ) { };
const options = { fetch( localStorage.getItem( 'url' ) + '/admin/api/deleteEvent', options ).then( res => {
method: 'post', if ( res.status === 200 ) {
body: JSON.stringify( { 'event': data.data } ), res.text().then( () => {
headers: { this.currentlyOpenMenu = '';
'Content-Type': 'application/json', this.loadData();
'charset': 'utf-8' } );
} }
}; } );
fetch( localStorage.getItem( 'url' ) + '/admin/api/createEvent', options ).then( res => {
if ( res.status === 200 ) {
res.text().then( () => {
this.currentlyOpenMenu = '';
this.loadData();
} );
} else if ( res.status === 409 ) {
this.$refs.popup.openPopup( 'This event does already exist. Please choose a different identifier!', {}, 'string' );
}
} );
}
} else if ( this.currentPopup === 'delete' ) {
console.log( data );
if ( data.status === 'ok' ) {
const options = {
method: 'post',
body: JSON.stringify( { 'event': this.currentlyOpenMenu } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/api/deleteEvent', options ).then( res => {
if ( res.status === 200 ) {
res.text().then( () => {
this.currentlyOpenMenu = '';
this.loadData();
} );
}
} );
}
} }
}, }
} },
}; }
};
</script> </script>

View File

@@ -36,164 +36,164 @@
</template> </template>
<script> <script>
import popups from '@/components/notifications/popups.vue'; import popups from '@/components/notifications/popups.vue';
import rightClickMenu from '@/components/settings/rightClickMenu.vue'; import rightClickMenu from '@/components/settings/rightClickMenu.vue';
export default { export default {
data () { data () {
return { return {
locations: { 'test':{ 'name':'TestLocation', 'locationID':'test', 'seatplan-enabled': true, 'seatplan': {} } }, locations: { 'test': { 'name': 'TestLocation', 'locationID': 'test', 'seatplan-enabled': true, 'seatplan': {} } },
currentlyOpenMenu: '', currentlyOpenMenu: '',
currentPopup: '', currentPopup: '',
updatedLocations: {} updatedLocations: {}
};
},
components: {
popups,
rightClickMenu,
},
methods: {
selectLocation ( locationID ) {
sessionStorage.setItem( 'locationID', locationID );
this.currentlyOpenMenu = locationID;
this.$refs.popup.openPopup( 'Settings for ' + this.locations[ locationID ][ 'name' ], {
'locationID': {
'display': 'Internal location name',
'id': 'locationID',
'tooltip': 'Give the location where the event takes place a name. This name will not be shown to the customers and is used for the backend and admin portal. Has to be unique',
'value': locationID,
'type': 'text',
},
'name': {
'display': 'Public location name',
'id': 'name',
'tooltip': 'The name of the location that is shown to the customers.',
'value': this.locations[ locationID ][ 'name' ],
'type': 'text',
},
'seatplan-enabled': {
'display': 'Use seat plan?',
'id': 'seatplan-enabled',
'tooltip': 'With this toggle you may specify whether or not this location has a seat plan or not.',
'value': this.locations[ locationID ][ 'seatplan-enabled' ],
'type': 'toggle',
},
}
, 'settings' );
},
addLocation () {
this.$refs.popup.openPopup( 'Add a new location', {
'locationID': {
'display': 'Internal location name',
'id': 'locationID',
'tooltip': 'Give the location where the event takes place a name. This name will not be shown to the customers and is used for the backend and admin portal. Has to be unique',
'value': '',
'type': 'text',
},
'name': {
'display': 'Public location name',
'id': 'name',
'tooltip': 'The name of the location that is shown to the customers.',
'value': '',
'type': 'text',
},
'seatplan-enabled': {
'display': 'Use seat plan?',
'id': 'seatplan-enabled',
'tooltip': 'With this toggle you may specify whether or not this location has a seat plan or not.',
'value': true,
'type': 'toggle',
},
'seatplanEditor': {
'display': 'Seat plan editor',
'id': 'seatplanEditor',
'tooltip': 'The seat plan editor allows you to create a seat plan that closely resembles the location you host the event in.',
'type': 'link',
'restrictions': {
'to': '/admin/seatplan',
'displayName': 'Edit seat plan'
}
},
}
, 'settings' );
},
openRightClickMenu( id, event, hasSeatplan ) {
this.currentlyOpenMenu = id;
if ( hasSeatplan ) {
this.$refs.rclk.openRightClickMenu( event, { 'edit': { 'command': 'editLocation', 'symbol': 'edit', 'display': 'Edit location' }, 'editor': { 'command': 'openEditor', 'symbol': 'tune', 'display': 'Edit seatplan' }, 'delete': { 'command': 'deleteLocation', 'symbol': 'delete', 'display': 'Delete location' } } );
} else {
this.$refs.rclk.openRightClickMenu( event, { 'edit': { 'command': 'editLocation', 'symbol': 'edit', 'display': 'Edit location' }, 'delete': { 'command': 'deleteLocation', 'symbol': 'delete', 'display': 'Delete location' } } );
} }
}, },
components: { executeCommand( command ) {
popups, if ( command === 'editLocation' ) {
rightClickMenu, this.selectLocation( this.currentlyOpenMenu );
} else if ( command === 'deleteLocation' ) {
this.$refs.popup.openPopup( 'Do you really want to delete the location ' + this.currentlyOpenMenu + '?', {}, 'confirm' );
this.currentPopup = 'delete';
} else if ( command === 'openEditor' ) {
sessionStorage.setItem( 'locationID', this.currentlyOpenMenu );
this.$router.push( '/admin/seatplan' );
}
}, },
methods: { handleData ( data ) {
selectLocation ( locationID ) { if ( this.currentPopup === 'delete' ) {
sessionStorage.setItem( 'locationID', locationID ); this.currentPopup = '';
this.currentlyOpenMenu = locationID; if ( data.status === 'ok' ) {
this.$refs.popup.openPopup( 'Settings for ' + this.locations[ locationID ][ 'name' ], { delete this.locations[ this.currentlyOpenMenu ];
'locationID': { const options = {
'display': 'Internal location name', method: 'post',
'id': 'locationID', body: JSON.stringify( { 'location': this.currentlyOpenMenu } ),
'tooltip':'Give the location where the event takes place a name. This name will not be shown to the customers and is used for the backend and admin portal. Has to be unique', headers: {
'value': locationID, 'Content-Type': 'application/json',
'type': 'text', 'charset': 'utf-8'
},
'name': {
'display': 'Public location name',
'id': 'name',
'tooltip':'The name of the location that is shown to the customers.',
'value': this.locations[ locationID ][ 'name' ],
'type': 'text',
},
'seatplan-enabled': {
'display': 'Use seat plan?',
'id': 'seatplan-enabled',
'tooltip':'With this toggle you may specify whether or not this location has a seat plan or not.',
'value': this.locations[ locationID ][ 'seatplan-enabled' ],
'type': 'toggle',
},
}
, 'settings' );
},
addLocation () {
this.$refs.popup.openPopup( 'Add a new location', {
'locationID': {
'display': 'Internal location name',
'id': 'locationID',
'tooltip':'Give the location where the event takes place a name. This name will not be shown to the customers and is used for the backend and admin portal. Has to be unique',
'value': '',
'type': 'text',
},
'name': {
'display': 'Public location name',
'id': 'name',
'tooltip':'The name of the location that is shown to the customers.',
'value': '',
'type': 'text',
},
'seatplan-enabled': {
'display': 'Use seat plan?',
'id': 'seatplan-enabled',
'tooltip':'With this toggle you may specify whether or not this location has a seat plan or not.',
'value': true,
'type': 'toggle',
},
'seatplanEditor': {
'display': 'Seat plan editor',
'id': 'seatplanEditor',
'tooltip':'The seat plan editor allows you to create a seat plan that closely resembles the location you host the event in.',
'type': 'link',
'restrictions': {
'to': '/admin/seatplan',
'displayName': 'Edit seat plan'
} }
}, };
fetch( localStorage.getItem( 'url' ) + '/admin/api/deleteLocation', options ).then( res => {
if ( res.status === 200 ) {
res.text().then( text => {
console.log( text );
} );
}
} );
} }
, 'settings' ); } else {
}, if ( data.status === 'settings' ) {
openRightClickMenu( id, event, hasSeatplan ) { if ( data.data.locationID !== this.currentlyOpenMenu && this.currentlyOpenMenu !== '' ) {
this.currentlyOpenMenu = id;
if ( hasSeatplan ) {
this.$refs.rclk.openRightClickMenu( event, { 'edit': { 'command': 'editLocation', 'symbol': 'edit', 'display': 'Edit location' }, 'editor': { 'command': 'openEditor', 'symbol': 'tune', 'display': 'Edit seatplan' }, 'delete': { 'command': 'deleteLocation', 'symbol': 'delete', 'display': 'Delete location' } } )
} else {
this.$refs.rclk.openRightClickMenu( event, { 'edit': { 'command': 'editLocation', 'symbol': 'edit', 'display': 'Edit location' }, 'delete': { 'command': 'deleteLocation', 'symbol': 'delete', 'display': 'Delete location' } } )
}
},
executeCommand( command ) {
if ( command === 'editLocation' ) {
this.selectLocation( this.currentlyOpenMenu );
} else if ( command === 'deleteLocation' ) {
this.$refs.popup.openPopup( 'Do you really want to delete the location ' + this.currentlyOpenMenu + '?', {}, 'confirm' );
this.currentPopup = 'delete';
} else if ( command === 'openEditor' ) {
sessionStorage.setItem( 'locationID', this.currentlyOpenMenu );
this.$router.push( '/admin/seatplan' );
}
},
handleData ( data ) {
if ( this.currentPopup === 'delete' ) {
this.currentPopup = '';
if ( data.status === 'ok' ) {
delete this.locations[ this.currentlyOpenMenu ]; delete this.locations[ this.currentlyOpenMenu ];
const options = { this.updatedLocations[ this.currentlyOpenMenu ] = data.data.locationID;
method: 'post',
body: JSON.stringify( { 'location': this.currentlyOpenMenu } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/api/deleteLocation', options ).then( res => {
if ( res.status === 200 ) {
res.text().then( text => {
console.log( text );
} );
}
} );
} }
} else { this.locations[ data.data.locationID ] = data.data;
if ( data.status === 'settings' ) { this.currentlyOpenMenu = '';
if ( data.data.locationID !== this.currentlyOpenMenu && this.currentlyOpenMenu !== '' ) { const options = {
delete this.locations[ this.currentlyOpenMenu ]; method: 'post',
this.updatedLocations[ this.currentlyOpenMenu ] = data.data.locationID; body: JSON.stringify( { 'updated': this.updatedLocations, 'data': this.locations } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
} }
this.locations[ data.data.locationID ] = data.data; };
this.currentlyOpenMenu = ''; fetch( localStorage.getItem( 'url' ) + '/admin/api/saveLocations', options ).then( res => {
const options = { if ( res.status === 200 ) {
method: 'post', res.text().then( text => {
body: JSON.stringify( { 'updated': this.updatedLocations, 'data': this.locations } ), console.log( text );
headers: { } );
'Content-Type': 'application/json', }
'charset': 'utf-8' } );
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/api/saveLocations', options ).then( res => {
if ( res.status === 200 ) {
res.text().then( text => {
console.log( text );
} );
}
} );
}
} }
}, }
}, },
created () { },
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getLocations' ).then( res => { created () {
res.json().then( data => { fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getLocations' ).then( res => {
this.locations = data; res.json().then( data => {
} ).catch( error => { this.locations = data;
console.error( error ); } ).catch( error => {
} ); console.error( error );
} ); } );
} } );
}; }
};
</script> </script>

View File

@@ -71,155 +71,155 @@
</template> </template>
<script> <script>
import PictureInput from 'vue-picture-input'; import PictureInput from 'vue-picture-input';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
import popups from '@/components/notifications/popups.vue'; import popups from '@/components/notifications/popups.vue';
export default { export default {
data () { data () {
return { return {
startPageTemplates: [], startPageTemplates: [],
startPageSettings: {}, startPageSettings: {},
selectedTemplate: '', selectedTemplate: '',
} };
},
components: {
PictureInput,
notifications,
popups,
},
methods: {
loadPageSettings() {
fetch( '/admin/getAPI/getStartPageSettings?name=' + this.selectedTemplate ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.startPageSettings = json[ 'options' ];
for ( let option in this.startPageSettings ) {
this.startPageSettings[ option ][ 'value' ] = json[ 'conf' ][ option ];
}
} );
}
} );
}, },
components: { save() {
PictureInput, let settings = {};
notifications, for ( let setting in this.startPageSettings ) {
popups, if ( this.startPageSettings[ setting ][ 'type' ] === 'image' ) {
}, if ( this.saveImage( this.startPageSettings[ setting ].id ) ) {
methods: { this.$refs.notification.createNotification( 'No image selected!', 5, 'error', 'normal' );
loadPageSettings() {
fetch( '/admin/getAPI/getStartPageSettings?name=' + this.selectedTemplate ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.startPageSettings = json[ 'options' ];
for ( let option in this.startPageSettings ) {
this.startPageSettings[ option ][ 'value' ] = json[ 'conf' ][ option ];
}
} );
} }
} ); } else {
}, if ( this.startPageSettings[ setting ][ 'value' ] ) {
save() { settings[ setting ] = this.startPageSettings[ setting ];
let settings = {};
for ( let setting in this.startPageSettings ) {
if ( this.startPageSettings[ setting ][ 'type' ] === 'image' ) {
if ( this.saveImage( this.startPageSettings[ setting ].id ) ) {
this.$refs.notification.createNotification( 'No image selected!', 5, 'error', 'normal' )
}
} else { } else {
if ( this.startPageSettings[ setting ][ 'value' ] ) { this.$refs.notification.createNotification( 'Required entries are missing!', 10, 'error', 'normal' );
settings[ setting ] = this.startPageSettings[ setting ]; return;
} else {
this.$refs.notification.createNotification( 'Required entries are missing!', 10, 'error', 'normal' );
return;
}
} }
} }
const options = { }
const options = {
method: 'post',
body: JSON.stringify( { 'preferences': settings, 'page': this.selectedTemplate } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/API/savePageSettings', options ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Saved settings successfully!', 5, 'ok', 'normal' );
} else {
this.$refs.notification.createNotification( 'An error occurred whilst saving', 10, 'error', 'normal' );
}
} );
},
enablePage() {
this.$refs.popups.openPopup( 'This operation will build the currently selected start page, enable it for use and overwrite any existing start pages.', {}, 'string' );
},
handlePopup( data ) {
if ( data.status === 'ok' ) {
const deploy = this.$refs.notification.createNotification( 'Building & deploying page...', 60, 'progress', 'normal' );
fetch( '/admin/getAPI/buildStartPage?page=' + this.selectedTemplate ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.cancelNotification( deploy );
this.$refs.notification.createNotification( 'Start page has been deployed successfully!', 5, 'ok', 'normal' );
} else if ( res.status === 412 ) {
this.$refs.notification.cancelNotification( deploy );
this.$refs.notification.createNotification( 'Some required fields for the page are missing. Did you hit save before clicking here?', 10, 'error', 'normal' );
} else {
console.error( res );
this.$refs.notification.cancelNotification( deploy );
this.$refs.notification.createNotification( 'An unknown error occurred whilst processing the request. Please try again later.', 5, 'error', 'normal' );
}
} );
}
},
saveImage( image ) {
if ( this.$refs[ image ][ 0 ].file ) {
console.log( 'saving image' );
let fd = new FormData();
fd.append( 'image', this.$refs[ image ][ 0 ].file );
let fetchOptions = {
method: 'post', method: 'post',
body: JSON.stringify( { 'preferences': settings, 'page': this.selectedTemplate } ), body: fd,
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
}; };
fetch( localStorage.getItem( 'url' ) + '/admin/API/savePageSettings', options ).then( res => { fetch( localStorage.getItem( 'url' ) + '/admin/pages/uploadImages?image=' + image + '&template=' + this.selectedTemplate, fetchOptions ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Saved settings successfully!', 5, 'ok', 'normal' ); return true;
} else { } else {
this.$refs.notification.createNotification( 'An error occurred whilst saving', 10, 'error', 'normal' ); this.$refs.notification.createNotification( 'There was an error uploading the image', 5, 'error', 'normal' );
} }
} ).catch( err => {
console.error( err );
} ); } );
}, return true;
enablePage() { } else {
this.$refs.popups.openPopup( 'This operation will build the currently selected start page, enable it for use and overwrite any existing start pages.', {}, 'string' ); return false;
},
handlePopup( data ) {
if ( data.status === 'ok' ) {
const deploy = this.$refs.notification.createNotification( 'Building & deploying page...', 60, 'progress', 'normal' );
fetch( '/admin/getAPI/buildStartPage?page=' + this.selectedTemplate ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.cancelNotification( deploy );
this.$refs.notification.createNotification( 'Start page has been deployed successfully!', 5, 'ok', 'normal' );
} else if ( res.status === 412 ) {
this.$refs.notification.cancelNotification( deploy );
this.$refs.notification.createNotification( 'Some required fields for the page are missing. Did you hit save before clicking here?', 10, 'error', 'normal' );
} else {
console.error( res );
this.$refs.notification.cancelNotification( deploy );
this.$refs.notification.createNotification( 'An unknown error occurred whilst processing the request. Please try again later.', 5, 'error', 'normal' );
}
} );
}
},
saveImage( image ) {
if ( this.$refs[ image ][ 0 ].file ) {
console.log( 'saving image' );
let fd = new FormData();
fd.append( 'image', this.$refs[ image ][ 0 ].file );
let fetchOptions = {
method: 'post',
body: fd,
};
fetch( localStorage.getItem( 'url' ) + '/admin/pages/uploadImages?image=' + image + '&template=' + this.selectedTemplate, fetchOptions ).then( res => {
if ( res.status === 200 ) {
return true;
} else {
this.$refs.notification.createNotification( 'There was an error uploading the image', 5, 'error', 'normal' );
}
} ).catch( err => {
console.error( err );
} );
return true;
} else {
return false;
}
},
saveLogo() {
if ( this.$refs.logoUpload.file ) {
let fd = new FormData();
fd.append( 'image', this.$refs.logoUpload.file );
let fetchOptions = {
method: 'post',
body: fd,
};
fetch( localStorage.getItem( 'url' ) + '/admin/logo/upload', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Logo uploaded successfully', 5, 'ok', 'normal' );
} else {
this.$refs.notification.createNotification( 'There was an error uploading the image', 5, 'error', 'normal' );
}
} ).catch( err => {
console.error( err );
} );
} else {
this.$refs.notification.createNotification( 'No logo selected. Please select one and try again!', 10, 'error', 'normal' );
}
} }
}, },
watch: { saveLogo() {
selectedTemplate( value ) { if ( this.$refs.logoUpload.file ) {
this.loadPageSettings(); let fd = new FormData();
fd.append( 'image', this.$refs.logoUpload.file );
let fetchOptions = {
method: 'post',
body: fd,
};
fetch( localStorage.getItem( 'url' ) + '/admin/logo/upload', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Logo uploaded successfully', 5, 'ok', 'normal' );
} else {
this.$refs.notification.createNotification( 'There was an error uploading the image', 5, 'error', 'normal' );
}
} ).catch( err => {
console.error( err );
} );
} else {
this.$refs.notification.createNotification( 'No logo selected. Please select one and try again!', 10, 'error', 'normal' );
} }
},
created () {
fetch( '/admin/getAPI/getAllStartPages' ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.startPageTemplates = json;
} );
}
} );
fetch( '/admin/getAPI/getSettings' ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.selectedTemplate = json[ 'startPage' ];
} );
}
} );
} }
}; },
watch: {
selectedTemplate( value ) {
this.loadPageSettings();
}
},
created () {
fetch( '/admin/getAPI/getAllStartPages' ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.startPageTemplates = json;
} );
}
} );
fetch( '/admin/getAPI/getSettings' ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.selectedTemplate = json[ 'startPage' ];
} );
}
} );
}
};
</script> </script>

View File

@@ -25,29 +25,29 @@
</template> </template>
<script> <script>
export default { export default {
data () { data () {
return { return {
allPlugins: {} allPlugins: {}
} };
}, },
methods: { methods: {
loadData () { loadData () {
fetch( '/admin/getAPI/getAllPlugins' ).then( res => { fetch( '/admin/getAPI/getAllPlugins' ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
res.json().then( json => { res.json().then( json => {
this.allPlugins = json; this.allPlugins = json;
} ).catch( err => { } ).catch( err => {
console.error( err ); console.error( err );
} ); } );
} }
} ); } );
}
},
created () {
this.loadData();
} }
}; },
created () {
this.loadData();
}
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -54,400 +54,400 @@
</template> </template>
<script> <script>
import settings from '@/components/settings/settings.vue'; import settings from '@/components/settings/settings.vue';
import popups from '@/components/notifications/popups.vue'; import popups from '@/components/notifications/popups.vue';
import rightClickMenu from '@/components/settings/rightClickMenu.vue'; import rightClickMenu from '@/components/settings/rightClickMenu.vue';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
export default { export default {
name: 'adminSettingsView', name: 'adminSettingsView',
components: { components: {
settings, settings,
popups, popups,
rightClickMenu, rightClickMenu,
notifications, notifications,
},
data () {
return {
adminAccounts: { 'janis': { 'username': 'janis', 'email': 'info@janishutz.com', 'permissions': [ ] }, 'admin': { 'username': 'admin', 'email': 'development@janishutz.com', 'permissions': [ ] } },
currentlyOpenMenu: '',
currentPopup: '',
settings: {
'2fa': {
'display': 'Require Two-Factor-Authentication of user',
'id': '2fa',
'tooltip': 'Control whether or not users are required to use Two-Factor-Authentication. Defaults to "User can decide", which is recommended',
'value': 'enforce',
'type': 'select',
'restrictions': {
'enforce': {
'displayName': 'Always require',
'value': 'enforce'
},
'allow': {
'displayName': 'User can decide',
'value': 'allow'
},
'disable': {
'displayName': 'Disable',
'value': 'disable'
},
}
},
'currency': {
'display': 'Currency',
'id': 'currency',
'tooltip': 'Specify a currency in which the prices are displayed to the customer. Defaults to USD. Please use valid currency codes.',
'value': 'USD',
'type': 'text',
},
'ticketTimeout': {
'display': 'Ticket Timeout (s)',
'id': 'ticketTimeout',
'tooltip': 'Specify how long the user has to be inactive for their order to be canceled. Time is to be specified in seconds',
'value': 900,
'type': 'number',
'restrictions': {
'min': 0,
'max': 10000,
}
},
'paymentGateway': {
'display': 'Select the payment gateway to use',
'id': 'paymentGateway',
'tooltip': 'With this setting you may change which payment gateway you want to use. You will need to provide details below! If you are not sure what this setting means, please click the link below.',
'value': 'stripe',
'type': 'select',
'restrictions': {
'payrexx': {
'displayName': 'Payrexx',
'value': 'payrexx'
},
'stripe': {
'displayName': 'Stripe',
'value': 'stripe'
},
}
},
// 'addressRequired': {
// 'display': 'Require user to provide address?',
// 'id': 'addressRequired',
// 'tooltip':'With this toggle you may specify whether or not a user has to provide their address when purchasing something. (Keep GDPR in mind when processing data!)',
// 'value': false,
// 'type': 'toggle',
// },
// 'phoneNumberRequired': {
// 'display': 'Require user to provide phone number?',
// 'id': 'phoneNumberRequired',
// 'tooltip':'With this toggle you may specify whether or not a user has to provide their phone number when purchasing something. (Keep GDPR in mind when processing data!)',
// 'value': false,
// 'type': 'toggle',
// },
// 'dobRequired': {
// 'display': 'Require user to provide their birth date?',
// 'id': 'dobRequired',
// 'tooltip':'With this toggle you may specify whether or not a user has to provide their date of birth when purchasing something. (Keep GDPR in mind when processing data!)',
// 'value': false,
// 'type': 'toggle',
// },
}
};
},
methods: {
showAccountSettings ( account ) {
this.currentPopup = 'permissions';
this.$refs.popup.openPopup( 'Edit user permissions for ' + this.adminAccounts[ account ][ 'username' ], {
'pagesSettings': {
'display': 'Modify pages',
'id': 'pagesSettings',
'tooltip': 'Change this setting to allow or disallow the selected user to access and change any settings of pages like the start page.',
'value': false,
'type': 'toggle',
},
'locationsSettings': {
'display': 'Location settings and seat plans',
'id': 'locationsSettings',
'tooltip': 'Change this setting to allow or disallow the selected user to modify, delete or create locations with their corresponding seat plans.',
'value': false,
'type': 'toggle',
},
'plugins': {
'display': 'Plugin management',
'id': 'plugins',
'tooltip': 'Change this setting to allow or disallow the selected user to install or uninstall plugins. Some plugins might allow you to set extra permissions inside of their settings panels',
'value': false,
'type': 'toggle',
},
'events': {
'display': 'Event management',
'id': 'events',
'tooltip': 'Change this setting to allow or disallow the selected user to install or uninstall plugins. Some plugins might allow you to set extra permissions inside of their settings panels',
'value': false,
'type': 'toggle',
},
}
, 'settings' );
}, },
data () { showPasswordSettings ( account ) {
return { this.currentlyOpenMenu = account;
adminAccounts: { 'janis': { 'username': 'janis', 'email': 'info@janishutz.com', 'permissions': [ ] }, 'admin': { 'username': 'admin', 'email': 'development@janishutz.com', 'permissions': [ ] } }, this.currentPopup = 'account';
currentlyOpenMenu: '', this.$refs.popup.openPopup( 'Edit user settings for ' + this.adminAccounts[ account ][ 'username' ], {
currentPopup: '', 'username': {
settings: { 'display': 'Username',
'2fa': { 'id': 'username',
'display': 'Require Two-Factor-Authentication of user', 'tooltip': 'Change the username for this user.',
'id': '2fa', 'value': this.adminAccounts[ account ][ 'username' ],
'tooltip':'Control whether or not users are required to use Two-Factor-Authentication. Defaults to "User can decide", which is recommended', 'type': 'text',
'value': 'enforce', },
'type': 'select', 'pass': {
'restrictions': { 'display': 'Password',
'enforce': { 'id': 'pass',
'displayName':'Always require', 'tooltip': 'Change the password for this user.',
'value': 'enforce' 'value': '',
}, 'type': 'password',
'allow': { },
'displayName':'User can decide', }, 'settings' );
'value': 'allow' },
}, showPaymentSettings () {
'disable': { this.currentPopup = 'payments';
'displayName':'Disable', fetch( '/admin/getAPI/getPaymentGatewaySettings' ).then( res => {
'value': 'disable' if ( res.status === 200 ) {
}, res.json().then( json => {
} this.$refs.popup.openPopup( 'Payment gateway settings for ' + json.gateway, json.data, 'settings' );
}, } );
'currency': { } else if ( res.status === 500 ) {
'display': 'Currency', this.$refs.notification.createNotification( 'This payment gateway does not appear to have settings', 10, 'error', 'normal' );
'id': 'currency',
'tooltip':'Specify a currency in which the prices are displayed to the customer. Defaults to USD. Please use valid currency codes.',
'value': 'USD',
'type': 'text',
},
'ticketTimeout': {
'display': 'Ticket Timeout (s)',
'id': 'ticketTimeout',
'tooltip': 'Specify how long the user has to be inactive for their order to be canceled. Time is to be specified in seconds',
'value': 900,
'type': 'number',
'restrictions': {
'min': 0,
'max': 10000,
}
},
'paymentGateway': {
'display': 'Select the payment gateway to use',
'id': 'paymentGateway',
'tooltip':'With this setting you may change which payment gateway you want to use. You will need to provide details below! If you are not sure what this setting means, please click the link below.',
'value': 'stripe',
'type': 'select',
'restrictions': {
'payrexx': {
'displayName':'Payrexx',
'value': 'payrexx'
},
'stripe': {
'displayName':'Stripe',
'value': 'stripe'
},
}
},
// 'addressRequired': {
// 'display': 'Require user to provide address?',
// 'id': 'addressRequired',
// 'tooltip':'With this toggle you may specify whether or not a user has to provide their address when purchasing something. (Keep GDPR in mind when processing data!)',
// 'value': false,
// 'type': 'toggle',
// },
// 'phoneNumberRequired': {
// 'display': 'Require user to provide phone number?',
// 'id': 'phoneNumberRequired',
// 'tooltip':'With this toggle you may specify whether or not a user has to provide their phone number when purchasing something. (Keep GDPR in mind when processing data!)',
// 'value': false,
// 'type': 'toggle',
// },
// 'dobRequired': {
// 'display': 'Require user to provide their birth date?',
// 'id': 'dobRequired',
// 'tooltip':'With this toggle you may specify whether or not a user has to provide their date of birth when purchasing something. (Keep GDPR in mind when processing data!)',
// 'value': false,
// 'type': 'toggle',
// },
} }
} );
},
createAccount() {
this.currentPopup = 'createAccount';
this.$refs.popup.openPopup( 'Create new admin user', {
// 'role': {
// 'display': 'User role',
// 'id': 'role',
// 'tooltip':'With this setting you can choose one of the preset permissions for users. Account management is only allowed for the root user.',
// 'value': 'eventManager',
// 'type': 'select',
// 'restrictions': {
// 'fullAccess': {
// 'value': 'fullAccess',
// 'displayName': 'Full Access'
// },
// 'eventManager': {
// 'value': 'eventManager',
// 'displayName': 'Event Manager'
// },
// 'entryControl': {
// 'value': 'entryControl',
// 'displayName': 'Entry Control'
// }
// }
// },
'username': {
'display': 'Username',
'id': 'username',
'tooltip': 'Add a username for this user',
'value': '',
'type': 'text',
},
'email': {
'display': 'Email',
'id': 'email',
'tooltip': 'Add an email-address for this user',
'value': '',
'type': 'text',
},
'pass': {
'display': 'Password',
'id': 'pass',
'tooltip': 'Create a password for this user.',
'value': '',
'type': 'password',
},
'two_fa': {
'display': 'Two Factor Authentication',
'id': 'two_fa',
'tooltip': 'With this setting you may change the 2FA Authentication should work for this user. Enhanced requires the user to enter a code, simple solely to click a link',
'value': 'enhanced',
'type': 'select',
'restrictions': {
'enhanced': {
'value': 'enhanced',
'displayName': 'Enhanced'
},
'eventManager': {
'value': 'simple',
'displayName': 'Simple'
},
'disabled': {
'value': 'disabled',
'displayName': 'Disabled'
}
}
},
}
, 'settings' );
},
executeCommand( command ) {
// if ( command === 'openPermissions' ) {
// this.currentPopup = 'account';
// this.showAccountSettings( this.currentlyOpenMenu );
// } else
if ( command === 'deleteUser' ) {
this.currentPopup = 'deleteUser';
this.$refs.popup.openPopup( 'Do you really want to delete the user ' + this.currentlyOpenMenu + '?', {}, 'confirm' );
} else if ( command === 'updatePassword' ) {
this.currentPopup = 'deleteUser';
this.showPasswordSettings( this.currentlyOpenMenu );
} }
}, },
methods: { handlePopupReturns( data ) {
showAccountSettings ( account ) { if ( data.status === 'cancel' ) {
this.currentPopup = 'permissions'; return;
this.$refs.popup.openPopup( 'Edit user permissions for ' + this.adminAccounts[ account ][ 'username' ], { } else if ( data.status === 'settings' ) {
'pagesSettings': { if ( this.currentPopup === 'account' ) {
'display': 'Modify pages', if ( data.data.username != '' ) {
'id': 'pagesSettings', let updatedData = data.data;
'tooltip':'Change this setting to allow or disallow the selected user to access and change any settings of pages like the start page.', if ( updatedData.pass == '' ) {
'value': false, delete updatedData[ 'pass' ];
'type': 'toggle',
},
'locationsSettings': {
'display': 'Location settings and seat plans',
'id': 'locationsSettings',
'tooltip':'Change this setting to allow or disallow the selected user to modify, delete or create locations with their corresponding seat plans.',
'value': false,
'type': 'toggle',
},
'plugins': {
'display': 'Plugin management',
'id': 'plugins',
'tooltip':'Change this setting to allow or disallow the selected user to install or uninstall plugins. Some plugins might allow you to set extra permissions inside of their settings panels',
'value': false,
'type': 'toggle',
},
'events': {
'display': 'Event management',
'id': 'events',
'tooltip':'Change this setting to allow or disallow the selected user to install or uninstall plugins. Some plugins might allow you to set extra permissions inside of their settings panels',
'value': false,
'type': 'toggle',
},
}
, 'settings' );
},
showPasswordSettings ( account ) {
this.currentlyOpenMenu = account;
this.currentPopup = 'account';
this.$refs.popup.openPopup( 'Edit user settings for ' + this.adminAccounts[ account ][ 'username' ], {
'username': {
'display': 'Username',
'id': 'username',
'tooltip':'Change the username for this user.',
'value': this.adminAccounts[ account ][ 'username' ],
'type': 'text',
},
'pass': {
'display': 'Password',
'id': 'pass',
'tooltip':'Change the password for this user.',
'value': '',
'type': 'password',
},
}, 'settings' );
},
showPaymentSettings () {
this.currentPopup = 'payments';
fetch( '/admin/getAPI/getPaymentGatewaySettings' ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.$refs.popup.openPopup( 'Payment gateway settings for ' + json.gateway, json.data, 'settings' );
} );
} else if ( res.status === 500 ) {
this.$refs.notification.createNotification( 'This payment gateway does not appear to have settings', 10, 'error', 'normal' );
}
} );
},
createAccount() {
this.currentPopup = 'createAccount';
this.$refs.popup.openPopup( 'Create new admin user', {
// 'role': {
// 'display': 'User role',
// 'id': 'role',
// 'tooltip':'With this setting you can choose one of the preset permissions for users. Account management is only allowed for the root user.',
// 'value': 'eventManager',
// 'type': 'select',
// 'restrictions': {
// 'fullAccess': {
// 'value': 'fullAccess',
// 'displayName': 'Full Access'
// },
// 'eventManager': {
// 'value': 'eventManager',
// 'displayName': 'Event Manager'
// },
// 'entryControl': {
// 'value': 'entryControl',
// 'displayName': 'Entry Control'
// }
// }
// },
'username': {
'display': 'Username',
'id': 'username',
'tooltip':'Add a username for this user',
'value': '',
'type': 'text',
},
'email': {
'display': 'Email',
'id': 'email',
'tooltip':'Add an email-address for this user',
'value': '',
'type': 'text',
},
'pass': {
'display': 'Password',
'id': 'pass',
'tooltip':'Create a password for this user.',
'value': '',
'type': 'password',
},
'two_fa': {
'display': 'Two Factor Authentication',
'id': 'two_fa',
'tooltip':'With this setting you may change the 2FA Authentication should work for this user. Enhanced requires the user to enter a code, simple solely to click a link',
'value': 'enhanced',
'type': 'select',
'restrictions': {
'enhanced': {
'value': 'enhanced',
'displayName': 'Enhanced'
},
'eventManager': {
'value': 'simple',
'displayName': 'Simple'
},
'disabled': {
'value': 'disabled',
'displayName': 'Disabled'
}
}
},
}
, 'settings' );
},
executeCommand( command ) {
// if ( command === 'openPermissions' ) {
// this.currentPopup = 'account';
// this.showAccountSettings( this.currentlyOpenMenu );
// } else
if ( command === 'deleteUser' ) {
this.currentPopup = 'deleteUser';
this.$refs.popup.openPopup( 'Do you really want to delete the user ' + this.currentlyOpenMenu + '?', {}, 'confirm' );
} else if ( command === 'updatePassword' ) {
this.currentPopup = 'deleteUser';
this.showPasswordSettings( this.currentlyOpenMenu );
}
},
handlePopupReturns( data ) {
if ( data.status === 'cancel' ) {
return;
} else if ( data.status === 'settings' ) {
if ( this.currentPopup === 'account' ) {
if ( data.data.username != '' ) {
let updatedData = data.data;
if ( updatedData.pass == '' ) {
delete updatedData[ 'pass' ];
}
updatedData[ 'email' ] = this.currentlyOpenMenu;
let fetchOptions = {
method: 'post',
body: JSON.stringify( updatedData ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( '/admin/API/updateAdminAccount', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Updated settings for admin account successfully', 5, 'ok', 'normal' );
this.loadAdminAccounts();
}
} );
}
} else if ( this.currentPopup === 'payments' ) {
for ( let setting in data.data ) {
if ( !data.data[ setting ] ) {
this.$refs.notification.createNotification( 'Settings for the payment gateway are missing!', 10, 'error', 'normal' );
this.showPaymentSettings();
return;
}
} }
updatedData[ 'email' ] = this.currentlyOpenMenu;
let fetchOptions = { let fetchOptions = {
method: 'post', method: 'post',
body: JSON.stringify( data.data ), body: JSON.stringify( updatedData ),
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'charset': 'utf-8' 'charset': 'utf-8'
} }
}; };
fetch( '/admin/API/updatePaymentGatewaySettings', fetchOptions ).then( res => { fetch( '/admin/API/updateAdminAccount', fetchOptions ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Payment gateway settings saved!', 5, 'ok', 'normal' ); this.$refs.notification.createNotification( 'Updated settings for admin account successfully', 5, 'ok', 'normal' );
}
} )
} else if ( this.currentPopup === 'createAccount' ) {
let fetchOptions = {
method: 'post',
body: JSON.stringify( data.data ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( '/admin/API/createAdminAccount', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Created new admin account successfully', 5, 'ok', 'normal' );
this.loadAdminAccounts(); this.loadAdminAccounts();
} }
} ); } );
} }
} else if ( data.status === 'ok' ) { } else if ( this.currentPopup === 'payments' ) {
if ( this.currentPopup === 'deleteUser' ) { for ( let setting in data.data ) {
let fetchOptions = { if ( !data.data[ setting ] ) {
method: 'post', this.$refs.notification.createNotification( 'Settings for the payment gateway are missing!', 10, 'error', 'normal' );
body: JSON.stringify( { 'email': this.currentlyOpenMenu } ), this.showPaymentSettings();
headers: { return;
'Content-Type': 'application/json', }
'charset': 'utf-8'
}
};
fetch( '/admin/API/deleteAdminAccount', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Admin account deleted successfully', 5, 'ok', 'normal' );
this.loadAdminAccounts();
}
} );
} }
} else { let fetchOptions = {
console.log( 'hi' ); method: 'post',
body: JSON.stringify( data.data ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( '/admin/API/updatePaymentGatewaySettings', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Payment gateway settings saved!', 5, 'ok', 'normal' );
}
} );
} else if ( this.currentPopup === 'createAccount' ) {
let fetchOptions = {
method: 'post',
body: JSON.stringify( data.data ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( '/admin/API/createAdminAccount', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Created new admin account successfully', 5, 'ok', 'normal' );
this.loadAdminAccounts();
}
} );
} }
}, } else if ( data.status === 'ok' ) {
openRightClickMenu( id, event ) { if ( this.currentPopup === 'deleteUser' ) {
this.$refs.rclk.openRightClickMenu( event, { let fetchOptions = {
// 'permissions': { 'command': 'openPermissions', 'symbol': 'edit', 'display': 'Edit permissions' }, method: 'post',
'password': { 'command': 'updatePassword', 'symbol': 'password', 'display': 'Edit account settings' }, body: JSON.stringify( { 'email': this.currentlyOpenMenu } ),
'delete': { 'command': 'deleteUser', 'symbol': 'delete', 'display': 'Delete User' } } ) headers: {
this.currentlyOpenMenu = id; 'Content-Type': 'application/json',
}, 'charset': 'utf-8'
loadData() { }
fetch( '/admin/getAPI/getSettings' ).then( res => { };
if ( res.status === 200 ) { fetch( '/admin/API/deleteAdminAccount', fetchOptions ).then( res => {
res.json().then( json => { if ( res.status === 200 ) {
this.settings[ '2fa' ].value = json.twoFA; this.$refs.notification.createNotification( 'Admin account deleted successfully', 5, 'ok', 'normal' );
this.settings.currency.value = json.currency; this.loadAdminAccounts();
this.settings.paymentGateway.value = json.payments; }
this.settings.ticketTimeout.value = json.ticketTimeout; } );
} ); }
} } else {
} ); console.log( 'hi' );
},
loadAdminAccounts () {
fetch( '/admin/getAPI/getAdminAccounts' ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
if ( json.status === 'ok' ) {
this.adminAccounts = {};
for ( let account in json.data ) {
this.adminAccounts[ json.data[ account ][ 'email' ] ] = json.data[ account ];
}
} else {
this.adminAccounts = {};
}
} );
}
} );
},
save() {
let fetchOptions = {
method: 'post',
body: JSON.stringify( {
'twoFA': this.settings[ '2fa' ].value,
'currency': this.settings.currency.value,
'payments': this.settings.paymentGateway.value,
'ticketTimeout': this.settings.ticketTimeout.value,
} ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/API/updateSettings', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Saved settings successfully. Restart libreevent to apply changes', 20, 'ok', 'normal' );
this.loadData();
}
} );
} }
}, },
created () { openRightClickMenu( id, event ) {
this.loadData(); this.$refs.rclk.openRightClickMenu( event, {
this.loadAdminAccounts(); // 'permissions': { 'command': 'openPermissions', 'symbol': 'edit', 'display': 'Edit permissions' },
'password': { 'command': 'updatePassword', 'symbol': 'password', 'display': 'Edit account settings' },
'delete': { 'command': 'deleteUser', 'symbol': 'delete', 'display': 'Delete User' } } );
this.currentlyOpenMenu = id;
},
loadData() {
fetch( '/admin/getAPI/getSettings' ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.settings[ '2fa' ].value = json.twoFA;
this.settings.currency.value = json.currency;
this.settings.paymentGateway.value = json.payments;
this.settings.ticketTimeout.value = json.ticketTimeout;
} );
}
} );
},
loadAdminAccounts () {
fetch( '/admin/getAPI/getAdminAccounts' ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
if ( json.status === 'ok' ) {
this.adminAccounts = {};
for ( let account in json.data ) {
this.adminAccounts[ json.data[ account ][ 'email' ] ] = json.data[ account ];
}
} else {
this.adminAccounts = {};
}
} );
}
} );
},
save() {
let fetchOptions = {
method: 'post',
body: JSON.stringify( {
'twoFA': this.settings[ '2fa' ].value,
'currency': this.settings.currency.value,
'payments': this.settings.paymentGateway.value,
'ticketTimeout': this.settings.ticketTimeout.value,
} ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/admin/API/updateSettings', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Saved settings successfully. Restart libreevent to apply changes', 20, 'ok', 'normal' );
this.loadData();
}
} );
} }
}; },
created () {
this.loadData();
this.loadAdminAccounts();
}
};
</script> </script>

View File

@@ -14,92 +14,92 @@
</template> </template>
<script> <script>
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from '@/stores/userStore';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
export default { export default {
name: 'twoFAAdmin', name: 'twoFAAdmin',
components: { components: {
notifications notifications
}, },
data () { data () {
return { return {
code: { '1': '', '2': '' } code: { '1': '', '2': '' }
} };
}, },
computed: { computed: {
...mapStores( useUserStore ), ...mapStores( useUserStore ),
}, },
created () { created () {
if ( this.userStore.getAdminTwoFACompliant ) { if ( this.userStore.getAdminTwoFACompliant ) {
if ( !!window.EventSource ) { if ( window.EventSource ) {
setTimeout( () => { setTimeout( () => {
let startNotification = this.$refs.notification.createNotification( 'Connecting to status service', 20, 'progress', 'normal' ); let startNotification = this.$refs.notification.createNotification( 'Connecting to status service', 20, 'progress', 'normal' );
let source = new EventSource( localStorage.getItem( 'url' ) + '/admin/2fa/check', { withCredentials: true } ); let source = new EventSource( localStorage.getItem( 'url' ) + '/admin/2fa/check', { withCredentials: true } );
let self = this; let self = this;
source.onmessage = ( e ) => { source.onmessage = ( e ) => {
if ( e.data === 'authenticated' ) { if ( e.data === 'authenticated' ) {
self.userStore.setAdminAuth( true ); self.userStore.setAdminAuth( true );
self.$router.push( '/admin' ); self.$router.push( '/admin' );
console.log( e.data ); console.log( e.data );
}
} }
};
source.onopen = e => { source.onopen = e => {
self.$refs.notification.createNotification( 'Connected to status service', 5, 'ok', 'normal' ); self.$refs.notification.createNotification( 'Connected to status service', 5, 'ok', 'normal' );
self.$refs.notification.cancelNotification( startNotification );
};
source.addEventListener( 'error', function( e ) {
if ( e.eventPhase == EventSource.CLOSED ) source.close();
if ( e.target.readyState == EventSource.CLOSED ) {
console.log( e );
self.$refs.notification.cancelNotification( startNotification ); self.$refs.notification.cancelNotification( startNotification );
}; self.$refs.notification.createNotification( 'Could not connect to status service', 5, 'error', 'normal' );
}
source.addEventListener( 'error', function(e) { }, false );
if ( e.eventPhase == EventSource.CLOSED ) source.close(); }, 300 );
if ( e.target.readyState == EventSource.CLOSED ) {
console.log( e );
self.$refs.notification.cancelNotification( startNotification );
self.$refs.notification.createNotification( 'Could not connect to status service', 5, 'error', 'normal' );
}
}, false)
}, 300 );
} else {
setTimeout( () => {
this.$refs.notification.createNotification( 'Unsupported browser detected. Redirection might take longer to occur!', 20, 'warning', 'normal' );
}, 300 );
// ping server every 5s to check if logged in
this.serverPing = setInterval( () => {
fetch( '/admin/2fa/ping' ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
if ( data ) {
if ( data.status === 'ok' ) {
this.userStore.setUserAuth( true );
this.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
}
}
} );
} else {
console.error( 'Request failed' );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
}
} ).catch( error => {
console.error( error );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
} );
}, 5000 );
}
let code = sessionStorage.getItem( '2faCode' ) ? sessionStorage.getItem( '2faCode' ) : '';
this.code = { '1': code.slice( 0, 3 ), '2': code.substring( 3 ) };
} else { } else {
if ( this.userStore.getAdminAuthenticated ) { setTimeout( () => {
this.$router.push( '/admin' ); this.$refs.notification.createNotification( 'Unsupported browser detected. Redirection might take longer to occur!', 20, 'warning', 'normal' );
} else { }, 300 );
this.$router.push( '/admin/login' ); // ping server every 5s to check if logged in
} this.serverPing = setInterval( () => {
fetch( '/admin/2fa/ping' ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
if ( data ) {
if ( data.status === 'ok' ) {
this.userStore.setUserAuth( true );
this.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
}
}
} );
} else {
console.error( 'Request failed' );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
}
} ).catch( error => {
console.error( error );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
} );
}, 5000 );
} }
}, let code = sessionStorage.getItem( '2faCode' ) ? sessionStorage.getItem( '2faCode' ) : '';
} this.code = { '1': code.slice( 0, 3 ), '2': code.substring( 3 ) };
} else {
if ( this.userStore.getAdminAuthenticated ) {
this.$router.push( '/admin' );
} else {
this.$router.push( '/admin/login' );
}
}
},
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -15,29 +15,29 @@
</template> </template>
<script> <script>
import window from '@/components/seatplan/editor/window.vue'; import window from '@/components/seatplan/editor/window.vue';
export default { export default {
data () { data () {
return { return {
location: '', location: '',
}; };
}, },
components: { components: {
window, window,
}, },
methods: { methods: {
checkLocationSelected () { checkLocationSelected () {
if ( !sessionStorage.getItem( 'locationID' ) ) { if ( !sessionStorage.getItem( 'locationID' ) ) {
this.$router.push( '/admin/locations' ); this.$router.push( '/admin/locations' );
}
this.location = sessionStorage.getItem( 'locationID' );
} }
}, this.location = sessionStorage.getItem( 'locationID' );
created() {
this.checkLocationSelected();
} }
}; },
created() {
this.checkLocationSelected();
}
};
</script> </script>
<style> <style>

View File

@@ -182,202 +182,311 @@
</style> </style>
<script> <script>
import settings from '@/components/settings/settings.vue'; import settings from '@/components/settings/settings.vue';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
import popups from '@/components/notifications/popups.vue'; import popups from '@/components/notifications/popups.vue';
import PictureInput from 'vue-picture-input'; import PictureInput from 'vue-picture-input';
export default { export default {
name: 'TicketsDetailsView', name: 'TicketsDetailsView',
components: { components: {
settings, settings,
notifications, notifications,
PictureInput, PictureInput,
popups, popups,
}, },
data() { data() {
return { return {
locations: {}, locations: {},
event: { 'name': 'Unnamed event', 'description': '', 'location': '', 'date': '', 'categories': {}, 'ageGroups': { '1':{ 'id': 1, 'name':'Child', 'age':'0 - 15.99' }, '2':{ 'id': 2, 'name': 'Adult' } }, 'maxTickets': 2, 'eventID': 'untitled' }, event: { 'name': 'Unnamed event', 'description': '', 'location': '', 'date': '', 'categories': {}, 'ageGroups': { '1': { 'id': 1, 'name': 'Child', 'age': '0 - 15.99' }, '2': { 'id': 2, 'name': 'Adult' } }, 'maxTickets': 2, 'eventID': 'untitled' },
specialSettings: { specialSettings: {
// 'guest-purchase': { // 'guest-purchase': {
// 'display': 'Enable guest purchase', // 'display': 'Enable guest purchase',
// 'id': 'guest-purchase', // 'id': 'guest-purchase',
// 'tooltip':'Allowing guest purchase means that a user does not have to create an account in order for them to be able to make a purchase. Default: On', // 'tooltip':'Allowing guest purchase means that a user does not have to create an account in order for them to be able to make a purchase. Default: On',
// 'value': true, // 'value': true,
// 'type': 'toggle' // 'type': 'toggle'
// }, // },
// 'overbooking': { // 'overbooking': {
// 'display': 'Enable overbooking of event', // 'display': 'Enable overbooking of event',
// 'id': 'overbooking', // 'id': 'overbooking',
// 'tooltip':'Allow more ticket reservations than you have tickets available. Currently only available for events without seatplans. Default: Off', // 'tooltip':'Allow more ticket reservations than you have tickets available. Currently only available for events without seatplans. Default: Off',
// 'value': false, // 'value': false,
// 'type': 'toggle' // 'type': 'toggle'
// }, // },
'maxTickets': { 'maxTickets': {
'display': 'Maximum ticket count per account', 'display': 'Maximum ticket count per account',
'id': 'maxTickets', 'id': 'maxTickets',
'tooltip':'With this setting you can control how many tickets a person can buy. Defaults to 0, which means do not limit.', 'tooltip': 'With this setting you can control how many tickets a person can buy. Defaults to 0, which means do not limit.',
'value': 0, 'value': 0,
'type': 'number', 'type': 'number',
'restrictions': { 'restrictions': {
'min': 0, 'min': 0,
'max': 100, 'max': 100,
} }
},
// 'requiredParameter': {
// 'display': 'Special requirements',
// 'id': 'requiredParameter',
// 'tooltip':'Set this parameter to require the user to provide a certain email domain, a special number or special string of characters. Defaults to None',
// 'value': 'none',
// 'type': 'select',
// 'restrictions': {
// 'none': {
// 'displayName':'None',
// 'value': 'none'
// },
// 'email': {
// 'displayName':'Email domain',
// 'value': 'email'
// },
// 'numbers': {
// 'displayName':'Number sequence',
// 'value': 'numbers'
// },
// 'string': {
// 'displayName':'Text sequence',
// 'value': 'string'
// },
// }
// },
// 'requiredParameterValue': {
// 'display': 'Special requirements values ',
// 'id': 'requiredParameterValue',
// 'tooltip':'Put a filter here, corresponding to your selection above. Please read the documentation on our website. See link below!',
// 'value': '',
// 'type': 'text',
// },
}, },
command: '', // 'requiredParameter': {
currentLocation: '', // 'display': 'Special requirements',
toDelete: '', // 'id': 'requiredParameter',
currency: 'USD', // 'tooltip':'Set this parameter to require the user to provide a certain email domain, a special number or special string of characters. Defaults to None',
hasLiveVersion: false, // 'value': 'none',
hasSeatPlan: true, // 'type': 'select',
totalSeats: 0, // 'restrictions': {
// 'none': {
// 'displayName':'None',
// 'value': 'none'
// },
// 'email': {
// 'displayName':'Email domain',
// 'value': 'email'
// },
// 'numbers': {
// 'displayName':'Number sequence',
// 'value': 'numbers'
// },
// 'string': {
// 'displayName':'Text sequence',
// 'value': 'string'
// },
// }
// },
// 'requiredParameterValue': {
// 'display': 'Special requirements values ',
// 'id': 'requiredParameterValue',
// 'tooltip':'Put a filter here, corresponding to your selection above. Please read the documentation on our website. See link below!',
// 'value': '',
// 'type': 'text',
// },
},
command: '',
currentLocation: '',
toDelete: '',
currency: 'USD',
hasLiveVersion: false,
hasSeatPlan: true,
totalSeats: 0,
};
},
created () {
this.loadData();
},
methods: {
loadData () {
if ( !sessionStorage.getItem( 'selectedTicket' ) ) {
this.$router.push( '/admin/events' );
}
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getCurrency' ).then( res => {
res.text().then( currency => {
this.currency = currency;
} );
} );
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getEventStatus' ).then( res => {
res.text().then( status => {
if ( status === 'true' ) {
this.hasLiveVersion = true;
} else {
this.hasLiveVersion = false;
}
} );
} );
this.eventID = sessionStorage.getItem( 'selectedTicket' );
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getLocations' ).then( res => {
res.json().then( data => {
this.locations = data;
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getEvent?event=' + this.eventID ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
this.event = data;
this.currentLocation = this.event.location;
const dt = this.event.date.split( 'T' );
this.event.date = dt[ 0 ];
this.event.time = dt[ 1 ].slice( 0, dt[ 1 ].length - 1 );
this.hasSeatPlan = this.locations[ this.event.location ] ? ( this.locations[ this.event.location ][ 'seatplan-enabled' ] ?? false ) : false;
} ).catch( error => {
console.error( error );
} );
} else if ( res.status === 404 ) {
this.$router.push( '/admin/events' );
}
} );
} ).catch( error => {
console.error( error );
} );
} );
},
saveImages() {
if ( this.$refs.logo.file && this.$refs.banner.file ) {
let fd = new FormData();
console.log( this.$refs.logo.file );
fd.append( 'image', this.$refs.logo.file );
fd.append( 'image', this.$refs.banner.file );
fd.append( 'logo', this.$refs.logo.file.name );
let fetchOptions = {
method: 'post',
body: fd,
};
fetch( localStorage.getItem( 'url' ) + '/admin/events/uploadImages?event=' + sessionStorage.getItem( 'selectedTicket' ) + '&image=' + 'logo', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Images saved successfully!', 5, 'ok', 'normal' );
}
} ).catch( err => {
console.error( err );
} );
} else {
this.$refs.notification.createNotification( 'No image selected!', 5, 'error', 'normal' );
} }
}, },
created () { save ( action ) {
this.loadData(); if ( Object.keys( this.event.ageGroups ).length > 0 && Object.keys( this.event.categories ).length > 0 ) {
}, for ( let ageGroup in this.event.ageGroups ) {
methods: { if ( this.event.ageGroups[ ageGroup ].name == '' ) {
loadData () { this.$refs.popups.openPopup( 'One or more age groups are missing their names. Please ensure that all age groups have a name and try again!', {}, 'string' );
if ( !sessionStorage.getItem( 'selectedTicket' ) ) { return;
this.$router.push( '/admin/events' ); }
} }
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getCurrency' ).then( res => {
res.text().then( currency => { let lowestPrice = 1000000;
this.currency = currency; let totalSeats = parseInt( this.locations[ this.event.location ].totalSeats ?? 0 );
} ); for ( let category in this.event.categories ) {
} ); for ( let price in this.event.categories[ category ].price ) {
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getEventStatus' ).then( res => { if ( this.event.categories[ category ].price[ price ] < 0.5 || ( !this.event.categories[ category ].ticketCount && this.hasSeatPlan ) ) {
res.text().then( status => { this.$refs.popups.openPopup( 'At least one of the prices for at least one of the categories is below the minimum of ' + this.currency + ' 0.5', {}, 'string' );
if ( status === 'true' ) { return;
}
if ( this.event.categories[ category ].price[ price ] < lowestPrice ) {
lowestPrice = this.event.categories[ category ].price[ price ];
}
}
totalSeats += parseInt( this.event.categories[ category ].ticketCount ?? 0 );
}
this.event[ 'startingPrice' ] = lowestPrice;
this.event[ 'currency' ] = this.currency;
this.event[ 'locationName' ] = this.locations[ this.event.location ].name;
this.event[ 'hasSeatplan' ] = this.hasSeatPlan;
this.event[ 'totalSeats' ] = totalSeats;
const fullDate = new Date( this.event.date + 'T' + this.event.time +'Z' );
this.event.date = fullDate.toISOString();
if ( !this.event.maxTickets ) {
this.event.maxTickets = this.totalSeats;
}
this.event.maxTickets = this.specialSettings[ 'maxTickets' ].value;
let url = localStorage.getItem( 'url' ) + '/admin/api/saveEvent';
if ( action === 'deploy' ) {
url = localStorage.getItem( 'url' ) + '/admin/api/deployEvent';
}
const options = {
method: 'post',
body: JSON.stringify( { 'event': this.event.eventID, 'eventData': this.event } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( url, options ).then( res => {
if ( res.status === 200 ) {
if ( action === 'deploy' ) {
this.$refs.notification.createNotification( 'Your event has been published successfully.', 5, 'ok', 'normal' );
fetch( '/getAPI/reloadData' ).catch( () => {} );
this.hasLiveVersion = true; this.hasLiveVersion = true;
} else { } else {
this.hasLiveVersion = false; this.$refs.notification.createNotification( 'Saved as draft successfully!', 5, 'ok', 'normal' );
} }
} ); this.loadData();
}
} ); } );
this.eventID = sessionStorage.getItem( 'selectedTicket' ); } else {
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getLocations' ).then( res => { this.$refs.popups.openPopup( 'Please ensure that you have at least one age group and one category defined!', {}, 'string' );
res.json().then( data => { }
this.locations = data; },
fetch( localStorage.getItem( 'url' ) + '/admin/getAPI/getEvent?event=' + this.eventID ).then( res => { addNew( type ) {
if ( res.status === 200 ) { if ( type === 'ageGroup' ) {
res.json().then( data => { this.$refs.popups.openPopup( 'Choose a name for the age group', {}, 'text' );
this.event = data; this.command = 'addAgeGroup';
this.currentLocation = this.event.location; } else if ( type === 'category' ) {
const dt = this.event.date.split( 'T' ); this.$refs.popups.openPopup( 'Choose a name for the new category', {}, 'number', Object.keys( this.event.categories ).length + 1 );
this.event.date = dt[ 0 ]; this.command = 'addCategory';
this.event.time = dt[ 1 ].slice( 0, dt[ 1 ].length - 1 ); }
this.hasSeatPlan = this.locations[ this.event.location ] ? ( this.locations[ this.event.location ][ 'seatplan-enabled' ] ?? false ) : false; },
} ).catch( error => { deleteObject( type, data ) {
console.error( error ); if ( type === 'ageGroup' ) {
} ); this.$refs.popups.openPopup( 'Do you really want to delete this age group?', {}, 'confirm' );
} else if ( res.status === 404 ) { this.command = 'deleteAgeGroup';
this.$router.push( '/admin/events' ); this.toDelete = data;
} } else if ( type === 'category' ) {
} ); this.$refs.popups.openPopup( 'Do you really want to delete this category', {}, 'confirm' );
} ).catch( error => { this.command = 'deleteCategory';
console.error( error ); this.toDelete = data;
} ); }
} ); },
}, handleLocationChange() {
saveImages() { if ( Object.keys( this.event.categories ).length > 1 && this.locations[ this.event.location ][ 'seatplan-enabled' ] ) {
if ( this.$refs.logo.file && this.$refs.banner.file ) { this.command = 'locationChange';
let fd = new FormData(); this.$refs.popups.openPopup( 'You have edited the categories of this location. Changing the location now leads to data loss.', {}, 'confirm' );
console.log( this.$refs.logo.file ); } else {
fd.append( 'image', this.$refs.logo.file ); this.command = 'locationChange';
fd.append( 'image', this.$refs.banner.file ); this.handlePopup( { 'status': 'ok' } );
fd.append( 'logo', this.$refs.logo.file.name ); }
let fetchOptions = { },
method: 'post', handlePopup( data ) {
body: fd, if ( data.status === 'ok' ) {
}; if ( this.command === 'addCategory' ) {
fetch( localStorage.getItem( 'url' ) + '/admin/events/uploadImages?event=' + sessionStorage.getItem( 'selectedTicket' ) + '&image=' + 'logo', fetchOptions ).then( res => { this.command = '';
if ( res.status === 200 ) { if ( !this.event.categories[ data.data ] ) {
this.$refs.notification.createNotification( 'Images saved successfully!', 5, 'ok', 'normal' ); this.event.categories[ data.data ] = { 'price': {}, 'bg': '#FFFFFF', 'fg': '#000000', 'name': 'Category ' + data.data, 'id': data.data, 'ticketCount': 1 };
for ( let ageGroup in this.event.ageGroups ) {
this.event.categories[ data.data ][ 'price' ][ ageGroup ] = 0;
} }
} ).catch( err => { } else {
console.error( err ); this.$refs.popups.openPopup( 'That category already exists!', {}, 'string' );
} ); }
} else { } else if ( this.command === 'addAgeGroup' ) {
this.$refs.notification.createNotification( 'No image selected!', 5, 'error', 'normal' ); this.command = '';
}
},
save ( action ) {
if ( Object.keys( this.event.ageGroups ).length > 0 && Object.keys( this.event.categories ).length > 0 ) {
for ( let ageGroup in this.event.ageGroups ) { for ( let ageGroup in this.event.ageGroups ) {
if ( this.event.ageGroups[ ageGroup ].name == '' ) { if ( this.event.ageGroups[ ageGroup ].name === data.data ) {
this.$refs.popups.openPopup( 'One or more age groups are missing their names. Please ensure that all age groups have a name and try again!', {}, 'string' ); this.$refs.popups.openPopup( 'That age group exists already', {}, 'string' );
return; return;
} }
} }
this.event.ageGroups[ Object.keys( this.event.categories ).length + 1 ] = { 'id': Object.keys( this.event.categories ).length + 1, 'name': data.data };
let lowestPrice = 1000000; for ( let ageGroup in this.event.ageGroups ) {
let totalSeats = parseInt( this.locations[ this.event.location ].totalSeats ?? 0 ); for ( let category in this.event.categories ) {
for ( let category in this.event.categories ) { if ( !this.event.categories[ category ][ 'price' ][ ageGroup ] ) {
for ( let price in this.event.categories[ category ].price ) { this.event.categories[ category ][ 'price' ][ ageGroup ] = 0;
if ( this.event.categories[ category ].price[ price ] < 0.5 || ( !this.event.categories[ category ].ticketCount && this.hasSeatPlan ) ) {
this.$refs.popups.openPopup( 'At least one of the prices for at least one of the categories is below the minimum of ' + this.currency + ' 0.5', {}, 'string' );
return;
} }
if ( this.event.categories[ category ].price[ price ] < lowestPrice ) {
lowestPrice = this.event.categories[ category ].price[ price ];
};
} }
totalSeats += parseInt( this.event.categories[ category ].ticketCount ?? 0 );
} }
} else if ( this.command === 'deleteCategory' ) {
this.event[ 'startingPrice' ] = lowestPrice; delete this.event.categories[ this.toDelete ];
this.event[ 'currency' ] = this.currency; } else if ( this.command === 'deleteAgeGroup' ) {
this.event[ 'locationName' ] = this.locations[ this.event.location ].name; delete this.event.ageGroups[ this.toDelete ];
this.event[ 'hasSeatplan' ] = this.hasSeatPlan; } else if ( this.command === 'locationChange' ) {
this.event[ 'totalSeats' ] = totalSeats; this.currentLocation = this.event.location;
const fullDate = new Date( this.event.date + 'T' + this.event.time +'Z' ); if ( this.locations[ this.event.location ][ 'seatplan-enabled' ] ) {
this.event.date = fullDate.toISOString(); fetch( '/admin/getAPI/getSeatplan?location=' + this.event.location ).then( res => {
if ( !this.event.maxTickets ) { if ( res.status === 200 ) {
this.event.maxTickets = this.totalSeats; res.json().then( json => {
} this.hasSeatPlan = this.locations[ this.event.location ][ 'seatplan-enabled' ] ?? false;
this.event.maxTickets = this.specialSettings[ 'maxTickets' ].value; this.event.categories = {};
let url = localStorage.getItem( 'url' ) + '/admin/api/saveEvent'; this.totalSeats = json.seatInfo.count;
if ( action === 'deploy' ) { for ( let element in json.data ) {
url = localStorage.getItem( 'url' ) + '/admin/api/deployEvent'; if ( json.data[ element ][ 'type' ] === 'seat' || json.data[ element ][ 'type' ] === 'stand' ) {
this.event.categories[ json.data[ element ][ 'category' ] ] = { 'price': {}, 'bg': '#FFFFFF', 'fg': '#000000', 'name': 'Category ' + json.data[ element ][ 'category' ], 'id': json.data[ element ][ 'category' ], 'ticketCount': 1 };
for ( let ageGroup in this.event.ageGroups ) {
this.event.categories[ json.data[ element ][ 'category' ] ][ 'price' ][ ageGroup ] = 0;
}
}
}
} );
}
} );
} }
} else if ( this.command === 'deployEvent' ) {
this.save( 'deploy' );
} else if ( this.command === 'undeployEvent' ) {
const options = { const options = {
method: 'post', method: 'post',
body: JSON.stringify( { 'event': this.event.eventID, 'eventData': this.event } ), body: JSON.stringify( { 'event': sessionStorage.getItem( this.event.eventID ) } ),
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'charset': 'utf-8' 'charset': 'utf-8'
@@ -385,160 +494,51 @@
}; };
fetch( url, options ).then( res => { fetch( url, options ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
if ( action === 'deploy' ) { this.hasLiveVersion = false;
this.$refs.notification.createNotification( 'Your event has been published successfully.', 5, 'ok', 'normal' ); this.$refs.notification.createNotification( 'Your event is no longer publicly visible!', 5, 'ok', 'normal' );
fetch( '/getAPI/reloadData' ).catch( () => {} ); } else {
this.hasLiveVersion = true; this.$refs.notification.createNotification( 'There was an error hiding your event', 5, 'error', 'normal' );
} else {
this.$refs.notification.createNotification( 'Saved as draft successfully!', 5, 'ok', 'normal' );
}
this.loadData();
} }
} ); } );
} else { } else if ( this.command === 'deleteEvent' ) {
this.$refs.popups.openPopup( 'Please ensure that you have at least one age group and one category defined!', {}, 'string' ); const options = {
} method: 'post',
}, body: JSON.stringify( { 'event': sessionStorage.getItem( this.event.eventID ) } ),
addNew( type ) { headers: {
if ( type === 'ageGroup' ) { 'Content-Type': 'application/json',
this.$refs.popups.openPopup( 'Choose a name for the age group', {}, 'text' ); 'charset': 'utf-8'
this.command = 'addAgeGroup'; }
} else if ( type === 'category' ) { };
this.$refs.popups.openPopup( 'Choose a name for the new category', {}, 'number', Object.keys( this.event.categories ).length + 1 ); fetch( url, options ).then( res => {
this.command = 'addCategory'; if ( res.status === 200 ) {
} this.$refs.notification.createNotification( 'Your event has been deleted successfully!', 5, 'ok', 'normal' );
}, setTimeout( () => {
deleteObject( type, data ) { this.$router.push( '/admin/events' );
if ( type === 'ageGroup' ) { }, 5000 );
this.$refs.popups.openPopup( 'Do you really want to delete this age group?', {}, 'confirm' );
this.command = 'deleteAgeGroup';
this.toDelete = data;
} else if ( type === 'category' ) {
this.$refs.popups.openPopup( 'Do you really want to delete this category', {}, 'confirm' );
this.command = 'deleteCategory';
this.toDelete = data;
}
},
handleLocationChange() {
if ( Object.keys( this.event.categories ).length > 1 && this.locations[ this.event.location ][ 'seatplan-enabled' ] ) {
this.command = 'locationChange';
this.$refs.popups.openPopup( 'You have edited the categories of this location. Changing the location now leads to data loss.', {}, 'confirm' );
} else {
this.command = 'locationChange';
this.handlePopup( { 'status': 'ok' } );
}
},
handlePopup( data ) {
if ( data.status === 'ok' ) {
if ( this.command === 'addCategory' ) {
this.command = '';
if ( !this.event.categories[ data.data ] ) {
this.event.categories[ data.data ] = { 'price': {}, 'bg': '#FFFFFF', 'fg': '#000000', 'name': 'Category ' + data.data, 'id': data.data, 'ticketCount': 1 };
for ( let ageGroup in this.event.ageGroups ) {
this.event.categories[ data.data ][ 'price' ][ ageGroup ] = 0;
}
} else { } else {
this.$refs.popups.openPopup( 'That category already exists!', {}, 'string' ); this.$refs.notification.createNotification( 'There was an error deleting your event', 5, 'error', 'normal' );
} }
} else if ( this.command === 'addAgeGroup' ) { } );
this.command = '';
for ( let ageGroup in this.event.ageGroups ) {
if ( this.event.ageGroups[ ageGroup ].name === data.data ) {
this.$refs.popups.openPopup( 'That age group exists already', {}, 'string' );
return;
}
}
this.event.ageGroups[ Object.keys( this.event.categories ).length + 1 ] = { 'id': Object.keys( this.event.categories ).length + 1, 'name': data.data };
for ( let ageGroup in this.event.ageGroups ) {
for ( let category in this.event.categories ) {
if ( !this.event.categories[ category ][ 'price' ][ ageGroup ] ) {
this.event.categories[ category ][ 'price' ][ ageGroup ] = 0;
}
}
}
} else if ( this.command === 'deleteCategory' ) {
delete this.event.categories[ this.toDelete ];
} else if ( this.command === 'deleteAgeGroup' ) {
delete this.event.ageGroups[ this.toDelete ];
} else if ( this.command === 'locationChange' ) {
this.currentLocation = this.event.location;
if ( this.locations[ this.event.location ][ 'seatplan-enabled' ] ) {
fetch( '/admin/getAPI/getSeatplan?location=' + this.event.location ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
this.hasSeatPlan = this.locations[ this.event.location ][ 'seatplan-enabled' ] ?? false;
this.event.categories = {};
this.totalSeats = json.seatInfo.count;
for ( let element in json.data ) {
if ( json.data[ element ][ 'type' ] === 'seat' || json.data[ element ][ 'type' ] === 'stand' ) {
this.event.categories[ json.data[ element ][ 'category' ] ] = { 'price': {}, 'bg': '#FFFFFF', 'fg': '#000000', 'name': 'Category ' + json.data[ element ][ 'category' ], 'id': json.data[ element ][ 'category' ], 'ticketCount': 1 };
for ( let ageGroup in this.event.ageGroups ) {
this.event.categories[ json.data[ element ][ 'category' ] ][ 'price' ][ ageGroup ] = 0;
}
}
}
} );
}
} )
}
} else if ( this.command === 'deployEvent' ) {
this.save( 'deploy' );
} else if ( this.command === 'undeployEvent' ) {
const options = {
method: 'post',
body: JSON.stringify( { 'event': sessionStorage.getItem( this.event.eventID ) } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( url, options ).then( res => {
if ( res.status === 200 ) {
this.hasLiveVersion = false;
this.$refs.notification.createNotification( 'Your event is no longer publicly visible!', 5, 'ok', 'normal' );
} else {
this.$refs.notification.createNotification( 'There was an error hiding your event', 5, 'error', 'normal' );
}
} );
} else if ( this.command === 'deleteEvent' ) {
const options = {
method: 'post',
body: JSON.stringify( { 'event': sessionStorage.getItem( this.event.eventID ) } ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( url, options ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Your event has been deleted successfully!', 5, 'ok', 'normal' );
setTimeout( () => {
this.$router.push( '/admin/events' );
}, 5000 );
} else {
this.$refs.notification.createNotification( 'There was an error deleting your event', 5, 'error', 'normal' );
}
} );
}
} else if ( data.status === 'cancel' ) {
if ( this.command === 'locationChange' ) {
this.event.location = this.currentLocation;
}
} }
}, } else if ( data.status === 'cancel' ) {
dangerZone( action ) { if ( this.command === 'locationChange' ) {
if ( action === 'deploy' ) { this.event.location = this.currentLocation;
this.$refs.popups.openPopup( 'Do you really want to go live with this event?', {}, 'confirm' );
this.command = 'deployEvent';
} else if ( action === 'undeploy' ) {
this.$refs.popups.openPopup( 'Do you really want to remove this event from the event listings?', {}, 'confirm' );
this.command = 'undeployEvent';
} else if ( type === 'delete' ) {
this.$refs.popups.openPopup( 'Do you really want to delete this event? This action cannot be undone', {}, 'confirm' );
this.command = 'deleteEvent';
} }
} }
},
dangerZone( action ) {
if ( action === 'deploy' ) {
this.$refs.popups.openPopup( 'Do you really want to go live with this event?', {}, 'confirm' );
this.command = 'deployEvent';
} else if ( action === 'undeploy' ) {
this.$refs.popups.openPopup( 'Do you really want to remove this event from the event listings?', {}, 'confirm' );
this.command = 'undeployEvent';
} else if ( type === 'delete' ) {
this.$refs.popups.openPopup( 'Do you really want to delete this event? This action cannot be undone', {}, 'confirm' );
this.command = 'deleteEvent';
}
} }
}; }
};
</script> </script>

File diff suppressed because one or more lines are too long

View File

@@ -14,16 +14,16 @@
</template> </template>
<script> <script>
export default { export default {
data () { data () {
return { return {
formData: {} formData: {}
} };
}, },
methods: { methods: {
setup () { setup () {
}
} }
}; }
};
</script> </script>

View File

@@ -114,126 +114,126 @@
</style> </style>
<script> <script>
import popups from '@/components/notifications/popups.vue'; import popups from '@/components/notifications/popups.vue';
export default { export default {
data() { data() {
return { return {
cart: {}, cart: {},
backend: { 'currency': 'CHF' }, backend: { 'currency': 'CHF' },
ticketToDelete: {}, ticketToDelete: {},
};
},
components: {
popups,
},
methods: {
calculateTotal () {
this.backend[ 'total' ] = 0;
for ( let event in this.cart ) {
for ( let ticket in this.cart[ event ][ 'tickets' ] ) {
this.backend[ 'total' ] += parseInt( this.cart[ event ][ 'tickets' ][ ticket ][ 'price' ] ) * parseInt( this.cart[ event ][ 'tickets' ][ ticket ][ 'count' ] ?? 1 );
}
} }
}, },
components: { deleteTicket ( ticketID, event, component ) {
popups, this.ticketToDelete[ 'event' ] = event;
this.ticketToDelete[ 'id' ] = ticketID;
this.ticketToDelete[ 'component' ] = component;
this.$refs.popups.openPopup( 'Do you really want to delete this ticket?', {}, 'confirm' );
}, },
methods: { verifyTicketDelete ( status ) {
calculateTotal () { if ( status === 'ok' ) {
this.backend[ 'total' ] = 0; if ( Object.keys( this.cart[ this.ticketToDelete.event ][ 'tickets' ] ).length > 1 ) {
for ( let event in this.cart ) { delete this.cart[ this.ticketToDelete.event ][ 'tickets' ][ this.ticketToDelete.id ];
for ( let ticket in this.cart[ event ][ 'tickets' ] ) { } else {
this.backend[ 'total' ] += parseInt( this.cart[ event ][ 'tickets' ][ ticket ][ 'price' ] ) * parseInt( this.cart[ event ][ 'tickets' ][ ticket ][ 'count' ] ?? 1 ); delete this.cart[ this.ticketToDelete.event ];
}
} }
}, const options = {
deleteTicket ( ticketID, event, component ) { method: 'post',
this.ticketToDelete[ 'event' ] = event; body: JSON.stringify( { 'id': this.ticketToDelete[ 'id' ], 'eventID': this.ticketToDelete[ 'event' ], 'component': this.ticketToDelete[ 'component' ] } ),
this.ticketToDelete[ 'id' ] = ticketID; headers: {
this.ticketToDelete[ 'component' ] = component; 'Content-Type': 'application/json',
this.$refs.popups.openPopup( 'Do you really want to delete this ticket?', {}, 'confirm' ); 'charset': 'utf-8'
},
verifyTicketDelete ( status ) {
if ( status === 'ok' ) {
if ( Object.keys( this.cart[ this.ticketToDelete.event ][ 'tickets' ] ).length > 1 ) {
delete this.cart[ this.ticketToDelete.event ][ 'tickets' ][ this.ticketToDelete.id ];
} else {
delete this.cart[ this.ticketToDelete.event ];
} }
const options = { };
method: 'post', fetch( localStorage.getItem( 'url' ) + '/API/deselectTicket', options );
body: JSON.stringify( { 'id': this.ticketToDelete[ 'id' ], 'eventID': this.ticketToDelete[ 'event' ], 'component': this.ticketToDelete[ 'component' ] } ), }
headers: { localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
'Content-Type': 'application/json', },
'charset': 'utf-8' seatChecks ( event ) {
let self = this;
let allSeatsAvailable = true;
fetch( localStorage.getItem( 'url' ) + '/getAPI/getReservedSeats?event=' + event ).then( res => {
if ( res.status === 200 ) {
let unavailableSeats = {};
res.json().then( data => {
for ( let seat in data.reserved ) {
if ( data.reserved[ seat ] ) {
if ( !unavailableSeats[ data.reserved[ seat ].component ] ) {
unavailableSeats[ data.reserved[ seat ].component ] = {};
}
unavailableSeats[ data.reserved[ seat ].component ][ data.reserved[ seat ].id ] = 'nav';
}
} }
}; for ( let seat in data.user ) {
fetch( localStorage.getItem( 'url' ) + '/API/deselectTicket', options ); if ( data.user[ seat ] ) {
if ( !unavailableSeats[ data.user[ seat ].component ] ) {
unavailableSeats[ data.user[ seat ].component ] = {};
}
unavailableSeats[ data.user[ seat ].component ][ data.user[ seat ].id ] = 'sel';
}
}
let tickets = {};
if ( this.cart[ event ] ) {
tickets = this.cart[ event ][ 'tickets' ];
}
if ( data.user ) {
for ( let element in tickets ) {
if ( !data.user[ element ] ) {
allSeatsAvailable = false;
if ( Object.keys( this.cart[ event ][ 'tickets' ] ).length > 1 ) {
delete this.cart[ event ][ 'tickets' ][ element ];
} else {
delete this.cart[ event ];
}
}
}
} else {
delete this.cart[ event ];
allSeatsAvailable = false;
}
this.unavailableSeats = unavailableSeats;
if ( !allSeatsAvailable ) {
setTimeout( () => {
self.$refs.popups.openPopup( 'We are sorry to tell you that since the last time the seat plan was refreshed, one or more of the seats you have selected has/have been taken.', {}, 'string' );
}, 500 );
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
}
} );
} else {
console.error( 'unable to load' );
} }
localStorage.setItem( 'cart', JSON.stringify( this.cart ) ); } );
},
seatChecks ( event ) {
let self = this;
let allSeatsAvailable = true;
fetch( localStorage.getItem( 'url' ) + '/getAPI/getReservedSeats?event=' + event ).then( res => {
if ( res.status === 200 ) {
let unavailableSeats = {};
res.json().then( data => {
for ( let seat in data.reserved ) {
if ( data.reserved[ seat ] ) {
if ( !unavailableSeats[ data.reserved[ seat ].component ] ) {
unavailableSeats[ data.reserved[ seat ].component ] = {};
}
unavailableSeats[ data.reserved[ seat ].component ][ data.reserved[ seat ].id ] = 'nav';
}
}
for ( let seat in data.user ) {
if ( data.user[ seat ] ) {
if ( !unavailableSeats[ data.user[ seat ].component ] ) {
unavailableSeats[ data.user[ seat ].component ] = {};
}
unavailableSeats[ data.user[ seat ].component ][ data.user[ seat ].id ] = 'sel';
}
}
let tickets = {};
if ( this.cart[ event ] ) {
tickets = this.cart[ event ][ 'tickets' ];
}
if ( data.user ) {
for ( let element in tickets ) {
if ( !data.user[ element ] ) {
allSeatsAvailable = false;
if ( Object.keys( this.cart[ event ][ 'tickets' ] ).length > 1 ) {
delete this.cart[ event ][ 'tickets' ][ element ];
} else {
delete this.cart[ event ];
}
}
}
} else {
delete this.cart[ event ];
allSeatsAvailable = false;
}
this.unavailableSeats = unavailableSeats;
if ( !allSeatsAvailable ) {
setTimeout( () => {
self.$refs.popups.openPopup( 'We are sorry to tell you that since the last time the seat plan was refreshed, one or more of the seats you have selected has/have been taken.', {}, 'string' );
}, 500 );
localStorage.setItem( 'cart', JSON.stringify( this.cart ) );
}
} );
} else {
console.error( 'unable to load' );
}
} );
},
}, },
created () { },
window.addEventListener( 'visibilitychange', ( e ) => { created () {
this.cart = localStorage.getItem( 'cart' ) ? JSON.parse( localStorage.getItem( 'cart' ) ): {}; window.addEventListener( 'visibilitychange', ( e ) => {
this.calculateTotal();
}, 1 );
this.cart = localStorage.getItem( 'cart' ) ? JSON.parse( localStorage.getItem( 'cart' ) ): {}; this.cart = localStorage.getItem( 'cart' ) ? JSON.parse( localStorage.getItem( 'cart' ) ): {};
this.calculateTotal(); this.calculateTotal();
for ( let event in this.cart ) { }, 1 );
this.seatChecks( event ); this.cart = localStorage.getItem( 'cart' ) ? JSON.parse( localStorage.getItem( 'cart' ) ): {};
} this.calculateTotal();
for ( let event in this.cart ) {
this.seatChecks( event );
} }
} }
};
</script> </script>

View File

@@ -14,15 +14,15 @@
</template> </template>
<script> <script>
export default { export default {
methods: { methods: {
}, },
data() { data() {
return {} return {};
}, },
created() { created() {
}
} }
};
</script> </script>

View File

@@ -99,56 +99,56 @@
</style> </style>
<script> <script>
export default { export default {
name: 'OrderView', name: 'OrderView',
methods: { methods: {
setActiveTicket ( id ) { setActiveTicket ( id ) {
sessionStorage.setItem( 'selectedTicket', id ); sessionStorage.setItem( 'selectedTicket', id );
sessionStorage.setItem( 'ticketData', JSON.stringify( { 'description': this.events[ id ][ 'description' ], 'name': this.events[ id ][ 'name' ], 'locationName': this.events[ id ][ 'locationName' ], 'date': this.events[ id ][ 'date' ] } ) ); sessionStorage.setItem( 'ticketData', JSON.stringify( { 'description': this.events[ id ][ 'description' ], 'name': this.events[ id ][ 'name' ], 'locationName': this.events[ id ][ 'locationName' ], 'date': this.events[ id ][ 'date' ] } ) );
sessionStorage.setItem( 'hasSeatplan', this.events[ id ][ 'hasSeatplan' ] ); sessionStorage.setItem( 'hasSeatplan', this.events[ id ][ 'hasSeatplan' ] );
}, },
loadEvents () { loadEvents () {
fetch( '/getAPI/getAllEvents' ).then( res => { fetch( '/getAPI/getAllEvents' ).then( res => {
res.json().then( dat => { res.json().then( dat => {
this.events = dat ?? {}; this.events = dat ?? {};
for ( let event in dat ) { for ( let event in dat ) {
if ( this.events[ event ][ 'description' ].length > 200 ) { if ( this.events[ event ][ 'description' ].length > 200 ) {
this.events[ event ][ 'shortDescription' ] = this.events[ event ][ 'description' ].slice( 0, 200 ) + '...'; this.events[ event ][ 'shortDescription' ] = this.events[ event ][ 'description' ].slice( 0, 200 ) + '...';
} else { } else {
this.events[ event ][ 'shortDescription' ] = this.events[ event ][ 'description' ]; this.events[ event ][ 'shortDescription' ] = this.events[ event ][ 'description' ];
}
this.events[ event ][ 'logo' ] = new URL( location.protocol + '//' + location.hostname + ':' + location.port + '/eventAssets/' + this.events[ event ].eventID + 'Logo.jpg' );
} }
} ); this.events[ event ][ 'logo' ] = new URL( location.protocol + '//' + location.hostname + ':' + location.port + '/eventAssets/' + this.events[ event ].eventID + 'Logo.jpg' );
} );
}
},
created() {
this.loadEvents();
},
data () {
return {
events: { 'test':{ 'name': 'TestEvent', 'description': 'This is a description for the TestEvent to test multiline support and proper positioning of the Fields', 'free': 2, 'maxTickets': 2, 'date':'2023-08-31T09:00:00Z', 'startingPrice':15, 'location': 'TestLocation', 'eventID': 'test', 'currency': 'CHF', 'logo': new URL( '/src/assets/logo.png', import.meta.url ).href }, 'test2':{ 'name': 'TestEvent2', 'description': 'This is a description for the TestEvent to test multiline support and proper positioning of the Fields', 'freeSeats': 2, 'maxSeats': 2, 'date':'2023-08-15T09:00:00Z', 'startingPrice':15, 'location': 'TestLocation', 'eventID': 'test2', 'currency': 'CHF', 'logo': new URL( '/src/assets/logo.png', import.meta.url ).href } },
today: new Date().getTime(),
locations: {},
}
},
computed: {
orderedEvents () {
let sorted = Object.keys( this.events ).sort( ( a, b ) => {
return new Date( this.events[ a ].date ).getTime() - new Date( this.events[ b ].date ).getTime();
} );
let rt = {};
for ( let element in sorted ) {
if ( new Date( this.events[ sorted[ element ] ].date ) > this.today ) {
rt[ sorted[ element ] ] = this.events[ sorted[ element ] ];
rt[ sorted[ element ] ][ 'dateString' ] = new Date( rt[ sorted[ element ] ][ 'date' ] ).toLocaleString();
} }
} );
} );
}
},
created() {
this.loadEvents();
},
data () {
return {
events: { 'test': { 'name': 'TestEvent', 'description': 'This is a description for the TestEvent to test multiline support and proper positioning of the Fields', 'free': 2, 'maxTickets': 2, 'date': '2023-08-31T09:00:00Z', 'startingPrice': 15, 'location': 'TestLocation', 'eventID': 'test', 'currency': 'CHF', 'logo': new URL( '/src/assets/logo.png', import.meta.url ).href }, 'test2': { 'name': 'TestEvent2', 'description': 'This is a description for the TestEvent to test multiline support and proper positioning of the Fields', 'freeSeats': 2, 'maxSeats': 2, 'date': '2023-08-15T09:00:00Z', 'startingPrice': 15, 'location': 'TestLocation', 'eventID': 'test2', 'currency': 'CHF', 'logo': new URL( '/src/assets/logo.png', import.meta.url ).href } },
today: new Date().getTime(),
locations: {},
};
},
computed: {
orderedEvents () {
let sorted = Object.keys( this.events ).sort( ( a, b ) => {
return new Date( this.events[ a ].date ).getTime() - new Date( this.events[ b ].date ).getTime();
} );
let rt = {};
for ( let element in sorted ) {
if ( new Date( this.events[ sorted[ element ] ].date ) > this.today ) {
rt[ sorted[ element ] ] = this.events[ sorted[ element ] ];
rt[ sorted[ element ] ][ 'dateString' ] = new Date( rt[ sorted[ element ] ][ 'date' ] ).toLocaleString();
} }
return rt;
} }
}, return rt;
}; }
},
};
</script> </script>

View File

@@ -22,88 +22,88 @@
</template> </template>
<script> <script>
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
export default { export default {
name: 'PaymentSuccessView', name: 'PaymentSuccessView',
components: { components: {
notifications notifications
}, },
methods: { methods: {
}, },
data() { data() {
return {} return {};
}, },
created() { created() {
if ( !!window.EventSource ) { if ( window.EventSource ) {
setTimeout( () => { setTimeout( () => {
let startNotification = this.$refs.notification.createNotification( 'Connecting to status service...', 20, 'progress', 'normal' ); let startNotification = this.$refs.notification.createNotification( 'Connecting to status service...', 20, 'progress', 'normal' );
let source = new EventSource( localStorage.getItem( 'url' ) + '/payments/status', { withCredentials: true } ); let source = new EventSource( localStorage.getItem( 'url' ) + '/payments/status', { withCredentials: true } );
let self = this; let self = this;
source.onmessage = ( e ) => { source.onmessage = ( e ) => {
if ( e.data === 'ready' ) { if ( e.data === 'ready' ) {
self.$refs.notification.cancelNotification( startNotification );
self.$refs.notification.createNotification( 'Your tickets are ready! Starting download...', 10, 'progress', 'normal' );
localStorage.removeItem( 'cart' );
fetch( '/getAPI/reloadData' ).catch( () => {} );
setTimeout( () => {
open( '/tickets/tickets.pdf' );
source.close();
}, 500 );
setTimeout( () => {
$( '#manual-download' ).slideDown( 500 );
}, 2000 );
} else if ( e.data === 'paymentOk' ) {
self.$refs.notification.createNotification( 'Your payment has been marked as completed!', 5, 'ok', 'normal' );
}
}
source.onopen = e => {
self.$refs.notification.createNotification( 'Connected to status service', 5, 'ok', 'normal' );
self.$refs.notification.cancelNotification( startNotification ); self.$refs.notification.cancelNotification( startNotification );
}; self.$refs.notification.createNotification( 'Your tickets are ready! Starting download...', 10, 'progress', 'normal' );
localStorage.removeItem( 'cart' );
fetch( '/getAPI/reloadData' ).catch( () => {} );
setTimeout( () => {
open( '/tickets/tickets.pdf' );
source.close();
}, 500 );
setTimeout( () => {
$( '#manual-download' ).slideDown( 500 );
}, 2000 );
} else if ( e.data === 'paymentOk' ) {
self.$refs.notification.createNotification( 'Your payment has been marked as completed!', 5, 'ok', 'normal' );
}
};
source.addEventListener( 'error', function( e ) { source.onopen = e => {
if ( e.eventPhase == EventSource.CLOSED ) source.close(); self.$refs.notification.createNotification( 'Connected to status service', 5, 'ok', 'normal' );
self.$refs.notification.cancelNotification( startNotification );
};
if ( e.target.readyState == EventSource.CLOSED ) { source.addEventListener( 'error', function( e ) {
self.$refs.notification.cancelNotification( startNotification ); if ( e.eventPhase == EventSource.CLOSED ) source.close();
self.$refs.notification.createNotification( 'Disconnected from status service', 20, 'info', 'normal' );
} if ( e.target.readyState == EventSource.CLOSED ) {
}, false ); self.$refs.notification.cancelNotification( startNotification );
}, 300 ); self.$refs.notification.createNotification( 'Disconnected from status service', 20, 'info', 'normal' );
} else { }
setTimeout( () => { }, false );
this.$refs.notification.createNotification( 'Unsupported browser detected. Ticket generation might take longer!', 20, 'warning', 'normal' ); }, 300 );
}, 300 ); } else {
// ping server every 5s to check if ticket ready setTimeout( () => {
this.serverPing = setInterval( () => { this.$refs.notification.createNotification( 'Unsupported browser detected. Ticket generation might take longer!', 20, 'warning', 'normal' );
fetch( '/payments/status/ping' ).then( res => { }, 300 );
if ( res.status === 200 ) { // ping server every 5s to check if ticket ready
res.json().then( data => { this.serverPing = setInterval( () => {
if ( data ) { fetch( '/payments/status/ping' ).then( res => {
if ( data.status === 'ready' ) { if ( res.status === 200 ) {
open( '/tickets/get' ); res.json().then( data => {
} else if ( data.status === 'paymentOk' ) { if ( data ) {
this.$refs.notification.createNotification( 'Your payment has been marked as completed!', 5, 'ok', 'normal' ); if ( data.status === 'ready' ) {
} open( '/tickets/get' );
} else if ( data.status === 'paymentOk' ) {
this.$refs.notification.createNotification( 'Your payment has been marked as completed!', 5, 'ok', 'normal' );
} }
} ); }
} else { } );
console.error( 'Request failed' ); } else {
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' ); console.error( 'Request failed' );
}
} ).catch( error => {
console.error( error );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' ); this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
} ); }
}, 5000 ); } ).catch( error => {
} console.error( error );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
} );
}, 5000 );
} }
} }
};
</script> </script>
<style> <style>

View File

@@ -171,7 +171,7 @@ export default {
backend: { 'currency': 'CHF' }, backend: { 'currency': 'CHF' },
cartNotEmpty: false, cartNotEmpty: false,
userData: {}, userData: {},
} };
}, },
components: { components: {
notifications, notifications,
@@ -189,7 +189,7 @@ export default {
for ( let event in cart ) { for ( let event in cart ) {
if ( Object.keys( cart[ event ][ 'tickets' ] ).length ) { if ( Object.keys( cart[ event ][ 'tickets' ] ).length ) {
this.cartNotEmpty = true; this.cartNotEmpty = true;
}; }
} }
if ( this.cartNotEmpty ) { if ( this.cartNotEmpty ) {

View File

@@ -158,26 +158,26 @@
</style> </style>
<script> <script>
export default { export default {
name: 'TicketsDetailsView', name: 'TicketsDetailsView',
created () { created () {
if ( !sessionStorage.getItem( 'selectedTicket' ) ) { if ( !sessionStorage.getItem( 'selectedTicket' ) ) {
this.$router.push( '/tickets' ); this.$router.push( '/tickets' );
}
this.eventID = sessionStorage.getItem( 'selectedTicket' );
this.event[ 'banner' ] = localStorage.getItem( 'url' ) + '/eventAssets/' + this.eventID + 'Banner.jpg';
this.event[ 'logo' ] = localStorage.getItem( 'url' ) + '/eventAssets/' + this.eventID + 'Logo.jpg';
const eventData = JSON.parse( sessionStorage.getItem( 'ticketData' ) );
this.event.name = eventData[ 'name' ];
this.event.date = eventData[ 'date' ];
this.event.description = eventData[ 'description' ];
this.event.location = eventData[ 'locationName' ];
},
data() {
return {
event: {},
}
} }
}; this.eventID = sessionStorage.getItem( 'selectedTicket' );
this.event[ 'banner' ] = localStorage.getItem( 'url' ) + '/eventAssets/' + this.eventID + 'Banner.jpg';
this.event[ 'logo' ] = localStorage.getItem( 'url' ) + '/eventAssets/' + this.eventID + 'Logo.jpg';
const eventData = JSON.parse( sessionStorage.getItem( 'ticketData' ) );
this.event.name = eventData[ 'name' ];
this.event.date = eventData[ 'date' ];
this.event.description = eventData[ 'description' ];
this.event.location = eventData[ 'locationName' ];
},
data() {
return {
event: {},
};
}
};
</script> </script>

View File

@@ -47,32 +47,32 @@
</style> </style>
<script> <script>
import seatplan from '@/components/seatplan/userApp/userWindow.vue'; import seatplan from '@/components/seatplan/userApp/userWindow.vue';
import noseatplan from '@/components/noseatplan.vue'; import noseatplan from '@/components/noseatplan.vue';
export default { export default {
name: 'TicketsDetailsView', name: 'TicketsDetailsView',
components: { components: {
seatplan, seatplan,
noseatplan noseatplan
}, },
data() { data() {
return { return {
hasSeatplan: false, hasSeatplan: false,
eventID: '', eventID: '',
} };
}, },
created () { created () {
if ( !sessionStorage.getItem( 'selectedTicket' ) ) { if ( !sessionStorage.getItem( 'selectedTicket' ) ) {
this.$router.push( '/tickets' ); this.$router.push( '/tickets' );
}
this.eventID = sessionStorage.getItem( 'selectedTicket' );
if ( sessionStorage.getItem( 'hasSeatplan' ) === 'false' ) {
this.hasSeatplan = false;
} else {
this.hasSeatplan = true;
}
} }
}; this.eventID = sessionStorage.getItem( 'selectedTicket' );
if ( sessionStorage.getItem( 'hasSeatplan' ) === 'false' ) {
this.hasSeatplan = false;
} else {
this.hasSeatplan = true;
}
}
};
</script> </script>

View File

@@ -107,163 +107,163 @@
</style> </style>
<script> <script>
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from '@/stores/userStore';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
import popups from '@/components/notifications/popups.vue'; import popups from '@/components/notifications/popups.vue';
export default { export default {
data () { data () {
return { return {
accountData: {}, accountData: {},
isEditingAccount: false, isEditingAccount: false,
emailStatus: '', emailStatus: '',
twoFASetting: 'allow', twoFASetting: 'allow',
};
},
components: {
notifications,
popups,
},
computed: {
...mapStores( useUserStore )
},
methods: {
logout() {
fetch( '/user/logout' ).then( res => {
this.$router.push( '/login' );
this.userStore.setUserAuth( false );
} );
},
toggleEditing () {
if ( this.isEditingAccount ) {
this.loadData();
} else {
this.emailLiveChecker();
} }
this.isEditingAccount = !this.isEditingAccount;
}, },
components: { save() {
notifications, let fetchOptions = {
popups, method: 'post',
}, body: JSON.stringify( this.accountData ),
computed: { headers: {
...mapStores( useUserStore ) 'Content-Type': 'application/json',
}, 'charset': 'utf-8'
methods: {
logout() {
fetch( '/user/logout' ).then( res => {
this.$router.push( '/login' );
this.userStore.setUserAuth( false );
} );
},
toggleEditing () {
if ( this.isEditingAccount ) {
this.loadData();
} else {
this.emailLiveChecker();
} }
this.isEditingAccount = !this.isEditingAccount; };
}, fetch( localStorage.getItem( 'url' ) + '/user/settings', fetchOptions ).then( res => {
save() { if ( res.status === 200 ) {
let fetchOptions = { this.$refs.notification.createNotification( 'Settings updated!', 5, 'ok', 'normal' );
method: 'post', this.isEditingAccount = false;
body: JSON.stringify( this.accountData ), } else {
headers: { this.$refs.notification.createNotification( 'An error occurred whilst updating the settings. Please retry', 20, 'error', 'normal' );
'Content-Type': 'application/json', }
'charset': 'utf-8' } );
} },
}; resendMailConfirmation() {
fetch( localStorage.getItem( 'url' ) + '/user/settings', fetchOptions ).then( res => { fetch( localStorage.getItem( 'url' ) + '/user/resendEmail' ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Settings updated!', 5, 'ok', 'normal' ); this.$refs.notification.createNotification( 'Confirmation email sent.', 5, 'ok', 'normal' );
this.isEditingAccount = false; } else {
} else { this.$refs.notification.createNotification( 'An error occurred whilst sending the confirmation mail. Please retry', 20, 'error', 'normal' );
this.$refs.notification.createNotification( 'An error occurred whilst updating the settings. Please retry', 20, 'error', 'normal' ); }
} } );
} ); },
}, emailLiveChecker () {
resendMailConfirmation() { setTimeout( () => {
fetch( localStorage.getItem( 'url' ) + '/user/resendEmail' ).then( res => { if ( this.checkEmail() ) {
if ( res.status === 200 ) { this.emailStatus = '';
this.$refs.notification.createNotification( 'Confirmation email sent.', 5, 'ok', 'normal' ); } else {
} else { this.emailStatus = 'Invalid email address';
this.$refs.notification.createNotification( 'An error occurred whilst sending the confirmation mail. Please retry', 20, 'error', 'normal' ); }
} }, 100 );
} ); },
}, checkEmail () {
emailLiveChecker () { const mail = this.accountData.email ?? '';
setTimeout( () => { let stat = { 'atPos': 0, 'topLevelPos': 0 };
if ( this.checkEmail() ) { for ( let l in mail ) {
this.emailStatus = ''; if ( stat[ 'atPos' ] > 0 ) {
} else { if ( mail[ l ] === '@' ) {
this.emailStatus = 'Invalid email address'; return false;
} } else if ( mail[ l ] === '.' ) {
}, 100 ); if ( stat[ 'topLevelPos' ] > 0 ) {
}, if ( l > stat[ 'topLevelPos' ] + 2 ) {
checkEmail () { stat[ 'topLevelPos' ] = parseInt( l );
const mail = this.accountData.email ?? '';
let stat = { 'atPos': 0, 'topLevelPos': 0 };
for ( let l in mail ) {
if ( stat[ 'atPos' ] > 0 ) {
if ( mail[ l ] === '@' ) {
return false;
} else if ( mail[ l ] === '.' ) {
if ( stat[ 'topLevelPos' ] > 0 ) {
if ( l > stat[ 'topLevelPos' ] + 2 ) {
stat[ 'topLevelPos' ] = parseInt( l );
} else {
return false;
}
} else {
if ( l > stat[ 'atPos' ] + 2 ) {
stat[ 'topLevelPos' ] = parseInt( l );
} else {
return false;
}
}
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '-' || mail[ l ] === '_' ) ) {
return false
}
} else {
if ( mail[ l ] === '@' ) {
if ( l > 2 ) {
stat[ 'atPos' ] = parseInt( l );
} else { } else {
return false; return false;
} }
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '.' || mail[ l ] === '-' || mail[ l ] == '_' ) ) { } else {
if ( l > stat[ 'atPos' ] + 2 ) {
stat[ 'topLevelPos' ] = parseInt( l );
} else {
return false;
}
}
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '-' || mail[ l ] === '_' ) ) {
return false;
}
} else {
if ( mail[ l ] === '@' ) {
if ( l > 2 ) {
stat[ 'atPos' ] = parseInt( l );
} else {
return false; return false;
} }
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '.' || mail[ l ] === '-' || mail[ l ] == '_' ) ) {
return false;
} }
} }
if ( mail.length > stat[ 'topLevelPos' ] + 2 && stat[ 'topLevelPos' ] > 0 && stat[ 'atPos' ] > 0 ) { }
return true; if ( mail.length > stat[ 'topLevelPos' ] + 2 && stat[ 'topLevelPos' ] > 0 && stat[ 'atPos' ] > 0 ) {
} else { return true;
return false; } else {
} return false;
},
loadData () {
// TODO: FUTURE Also get all orders of user (using join functions)
fetch( localStorage.getItem( 'url' ) + '/settings/2fa' ).then( res => {
if ( res.status === 200 ) {
res.text().then( text => {
this.twoFASetting = text;
} );
}
} );
fetch( localStorage.getItem( 'url' ) + '/user/details' ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
if ( data.status ) {
this.accountData = data.data;
if ( !this.accountData.two_fa ) {
this.accountData.two_fa = 'disabled';
}
if ( !data.data.mail_confirmed ) {
setTimeout( () => {
this.$refs.notification.createNotification( 'Your account is unverified. Please confirm your email using the link we have sent to your email!', 20, 'info', 'normal' );
}, 1000 );
}
} else {
this.userStore.setUserAuth( false );
this.userStore.setUser2fa( false );
this.$router.push( '/login' );
}
} );
} else if ( res.status === 403 || res.status === 404 || res.status === 500 ) {
this.userStore.setUserAuth( false );
this.userStore.setUser2fa( false );
this.$router.push( '/login' );
}
} ).catch( err => {
console.warn( '[ AccountView ] Loading failed with the following message: ' + err );
} );
if ( this.userStore.getUserTwoFACompliant ) {
this.userStore.setUser2fa( false );
}
} }
}, },
created () { loadData () {
this.loadData(); // TODO: FUTURE Also get all orders of user (using join functions)
fetch( localStorage.getItem( 'url' ) + '/settings/2fa' ).then( res => {
if ( res.status === 200 ) {
res.text().then( text => {
this.twoFASetting = text;
} );
}
} );
fetch( localStorage.getItem( 'url' ) + '/user/details' ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
if ( data.status ) {
this.accountData = data.data;
if ( !this.accountData.two_fa ) {
this.accountData.two_fa = 'disabled';
}
if ( !data.data.mail_confirmed ) {
setTimeout( () => {
this.$refs.notification.createNotification( 'Your account is unverified. Please confirm your email using the link we have sent to your email!', 20, 'info', 'normal' );
}, 1000 );
}
} else {
this.userStore.setUserAuth( false );
this.userStore.setUser2fa( false );
this.$router.push( '/login' );
}
} );
} else if ( res.status === 403 || res.status === 404 || res.status === 500 ) {
this.userStore.setUserAuth( false );
this.userStore.setUser2fa( false );
this.$router.push( '/login' );
}
} ).catch( err => {
console.warn( '[ AccountView ] Loading failed with the following message: ' + err );
} );
if ( this.userStore.getUserTwoFACompliant ) {
this.userStore.setUser2fa( false );
}
} }
},
created () {
this.loadData();
} }
};
</script> </script>

View File

@@ -26,63 +26,63 @@
</template> </template>
<script> <script>
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from '@/stores/userStore';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
export default { export default {
data () { data () {
return { return {
formData: {} formData: {}
};
},
components: {
notifications,
},
computed: {
...mapStores( useUserStore )
},
methods: {
login () {
if ( this.formData.mail ) {
if ( this.formData.password ) {
let progress = this.$refs.notification.createNotification( 'Logging you in', 20, 'progress', 'normal' );
let fetchOptions = {
method: 'post',
body: JSON.stringify( this.formData ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/user/login', fetchOptions ).then( res => {
res.json().then( json => {
if ( json.status === 'ok' ) {
this.userStore.setUserAuth( true );
this.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
sessionStorage.removeItem( 'redirect' );
} else if ( json.status === '2fa' ) {
this.userStore.setUser2fa( true );
this.$router.push( '/twoFactors' );
} else if ( json.status === '2fa+' ) {
this.userStore.setUser2fa( true );
sessionStorage.setItem( '2faCode', json.code );
this.$router.push( '/twoFactors' );
} else {
this.$refs.notification.cancelNotification( progress );
this.$refs.notification.createNotification( 'The credentials you provided do not match our records.', 5, 'error', 'normal' );
}
} );
} );
} else {
this.$refs.notification.createNotification( 'A password is required to log in', 5, 'error', 'normal' );
}
} else {
this.$refs.notification.createNotification( 'An email address is required to log in', 5, 'error', 'normal' );
} }
}, },
components: { },
notifications, };
},
computed: {
...mapStores( useUserStore )
},
methods: {
login () {
if ( this.formData.mail ) {
if ( this.formData.password ) {
let progress = this.$refs.notification.createNotification( 'Logging you in', 20, 'progress', 'normal' );
let fetchOptions = {
method: 'post',
body: JSON.stringify( this.formData ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/user/login', fetchOptions ).then( res => {
res.json().then( json => {
if ( json.status === 'ok' ) {
this.userStore.setUserAuth( true );
this.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
sessionStorage.removeItem( 'redirect' );
} else if ( json.status === '2fa' ) {
this.userStore.setUser2fa( true );
this.$router.push( '/twoFactors' );
} else if ( json.status === '2fa+' ) {
this.userStore.setUser2fa( true );
sessionStorage.setItem( '2faCode', json.code );
this.$router.push( '/twoFactors' );
} else {
this.$refs.notification.cancelNotification( progress );
this.$refs.notification.createNotification( 'The credentials you provided do not match our records.', 5, 'error', 'normal' );
}
} );
} );
} else {
this.$refs.notification.createNotification( 'A password is required to log in', 5, 'error', 'normal' );
}
} else {
this.$refs.notification.createNotification( 'An email address is required to log in', 5, 'error', 'normal' );
}
},
},
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -28,41 +28,41 @@
</style> </style>
<script> <script>
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
export default { export default {
data () { data () {
return { return {
email: '', email: '',
} };
}, },
components: { components: {
notifications, notifications,
}, },
methods: { methods: {
reset() { reset() {
const startNotification = this.$refs.notification.createNotification( 'Starting password reset', 20, 'progress', 'normal' ); const startNotification = this.$refs.notification.createNotification( 'Starting password reset', 20, 'progress', 'normal' );
let fetchOptions = { let fetchOptions = {
method: 'post', method: 'post',
body: JSON.stringify( {'email': this.email } ), body: JSON.stringify( {'email': this.email } ),
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'charset': 'utf-8' 'charset': 'utf-8'
} }
}; };
fetch( localStorage.getItem( 'url' ) + '/API/resetPW', fetchOptions ).then( res => { fetch( localStorage.getItem( 'url' ) + '/API/resetPW', fetchOptions ).then( res => {
if ( res.status !== 200 ) { if ( res.status !== 200 ) {
this.$refs.notification.cancelNotification( startNotification ); this.$refs.notification.cancelNotification( startNotification );
this.$refs.notification.createNotification( 'An account with this email address does not exist.', 5, 'error', 'normal' ); this.$refs.notification.createNotification( 'An account with this email address does not exist.', 5, 'error', 'normal' );
} else { } else {
this.$refs.notification.cancelNotification( startNotification ); this.$refs.notification.cancelNotification( startNotification );
this.$refs.notification.createNotification( 'Password reset email sent. Please follow the instructions given there.', 30, 'ok', 'normal' ); this.$refs.notification.createNotification( 'Password reset email sent. Please follow the instructions given there.', 30, 'ok', 'normal' );
setTimeout( () => { setTimeout( () => {
location.href = '/login'; location.href = '/login';
}, 10000 ); }, 10000 );
} }
} ); } );
}
} }
} }
};
</script> </script>

View File

@@ -53,136 +53,136 @@
</template> </template>
<script> <script>
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from '@/stores/userStore';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
export default { export default {
data () { data () {
return { return {
formData: {}, formData: {},
emailStatus: '', emailStatus: '',
} };
},
components: {
notifications,
},
computed: {
...mapStores( useUserStore )
},
methods: {
emailLiveChecker () {
setTimeout( () => {
if ( this.checkEmail() ) {
this.emailStatus = '';
} else {
this.emailStatus = 'Invalid email address';
}
}, 100 );
}, },
components: { checkEmail () {
notifications, const mail = this.formData.mail ?? '';
}, let stat = { 'atPos': 0, 'topLevelPos': 0 };
computed: { for ( let l in mail ) {
...mapStores( useUserStore ) if ( stat[ 'atPos' ] > 0 ) {
}, if ( mail[ l ] === '@' ) {
methods: { return false;
emailLiveChecker () { } else if ( mail[ l ] === '.' ) {
setTimeout( () => { if ( stat[ 'topLevelPos' ] > 0 ) {
if ( this.checkEmail() ) { if ( l > stat[ 'topLevelPos' ] + 2 ) {
this.emailStatus = ''; stat[ 'topLevelPos' ] = parseInt( l );
} else {
this.emailStatus = 'Invalid email address';
}
}, 100 );
},
checkEmail () {
const mail = this.formData.mail ?? '';
let stat = { 'atPos': 0, 'topLevelPos': 0 };
for ( let l in mail ) {
if ( stat[ 'atPos' ] > 0 ) {
if ( mail[ l ] === '@' ) {
return false;
} else if ( mail[ l ] === '.' ) {
if ( stat[ 'topLevelPos' ] > 0 ) {
if ( l > stat[ 'topLevelPos' ] + 2 ) {
stat[ 'topLevelPos' ] = parseInt( l );
} else {
return false;
}
} else {
if ( l > stat[ 'atPos' ] + 2 ) {
stat[ 'topLevelPos' ] = parseInt( l );
} else {
return false;
}
}
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '-' || mail[ l ] === '_' ) ) {
return false
}
} else {
if ( mail[ l ] === '@' ) {
if ( l > 2 ) {
stat[ 'atPos' ] = parseInt( l );
} else { } else {
return false; return false;
} }
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '.' || mail[ l ] === '-' || mail[ l ] == '_' ) ) { } else {
if ( l > stat[ 'atPos' ] + 2 ) {
stat[ 'topLevelPos' ] = parseInt( l );
} else {
return false;
}
}
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '-' || mail[ l ] === '_' ) ) {
return false;
}
} else {
if ( mail[ l ] === '@' ) {
if ( l > 2 ) {
stat[ 'atPos' ] = parseInt( l );
} else {
return false; return false;
} }
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '.' || mail[ l ] === '-' || mail[ l ] == '_' ) ) {
return false;
} }
} }
if ( mail.length > stat[ 'topLevelPos' ] + 2 && stat[ 'topLevelPos' ] > 0 && stat[ 'atPos' ] > 0 ) { }
return true; if ( mail.length > stat[ 'topLevelPos' ] + 2 && stat[ 'topLevelPos' ] > 0 && stat[ 'atPos' ] > 0 ) {
} else { return true;
return false; } else {
} return false;
},
signup () {
if ( !this.formData.mail ) {
this.$refs.notification.createNotification( 'An email address is required to sign up', 5, 'error', 'normal' );
return;
}
if ( !this.formData.password ) {
this.$refs.notification.createNotification( 'A password is required to sign up', 5, 'error', 'normal' );
return;
}
if ( !this.formData.password2 ) {
this.$refs.notification.createNotification( 'Please confirm your password using the "Confirm password field"', 5, 'error', 'normal' );
return;
}
if ( this.formData.password !== this.formData.password2 ) {
this.$refs.notification.createNotification( 'The passwords provided do not match. Please ensure they are the same', 5, 'error', 'normal' );
return;
}
if ( !this.formData.country ) {
this.$refs.notification.createNotification( 'Please provide the country you live in.', 5, 'error', 'normal' );
return;
}
if ( !this.formData.name && !this.formData.firstName ) {
this.$refs.notification.createNotification( 'Please provide your first and last name!', 5, 'error', 'normal' );
return;
}
if ( !this.checkEmail() ) {
this.$refs.notification.createNotification( 'This email address is not an email address', 5, 'error', 'normal' );
return;
}
let progress = this.$refs.notification.createNotification( 'Signing up...', 20, 'progress', 'normal' );
let fetchOptions = {
method: 'post',
body: JSON.stringify( this.formData ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/user/signup', fetchOptions ).then( res => {
res.text().then( status => {
if ( status === 'ok' ) {
this.$refs.notification.cancelNotification( progress );
this.$refs.notification.createNotification( 'Signed up successfully. We have sent you an email. Please confirm it to finish sign-up', 5, 'ok', 'normal' );
setTimeout( () => {
this.userStore.setUserAuth( true );
this.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
sessionStorage.removeItem( 'redirect' );
}, 5000 );
} else if ( status === 'exists' ) {
this.$refs.notification.cancelNotification( progress );
this.$refs.notification.createNotification( 'An account with this email address already exists. Please log in using it.', 5, 'error', 'normal' );
this.$refs.notification.createNotification( 'If you do not remember your password, reset it!', 5, 'error', 'normal' );
} else {
console.log( status );
}
} );
} );
} }
}, },
} signup () {
if ( !this.formData.mail ) {
this.$refs.notification.createNotification( 'An email address is required to sign up', 5, 'error', 'normal' );
return;
}
if ( !this.formData.password ) {
this.$refs.notification.createNotification( 'A password is required to sign up', 5, 'error', 'normal' );
return;
}
if ( !this.formData.password2 ) {
this.$refs.notification.createNotification( 'Please confirm your password using the "Confirm password field"', 5, 'error', 'normal' );
return;
}
if ( this.formData.password !== this.formData.password2 ) {
this.$refs.notification.createNotification( 'The passwords provided do not match. Please ensure they are the same', 5, 'error', 'normal' );
return;
}
if ( !this.formData.country ) {
this.$refs.notification.createNotification( 'Please provide the country you live in.', 5, 'error', 'normal' );
return;
}
if ( !this.formData.name && !this.formData.firstName ) {
this.$refs.notification.createNotification( 'Please provide your first and last name!', 5, 'error', 'normal' );
return;
}
if ( !this.checkEmail() ) {
this.$refs.notification.createNotification( 'This email address is not an email address', 5, 'error', 'normal' );
return;
}
let progress = this.$refs.notification.createNotification( 'Signing up...', 20, 'progress', 'normal' );
let fetchOptions = {
method: 'post',
body: JSON.stringify( this.formData ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/user/signup', fetchOptions ).then( res => {
res.text().then( status => {
if ( status === 'ok' ) {
this.$refs.notification.cancelNotification( progress );
this.$refs.notification.createNotification( 'Signed up successfully. We have sent you an email. Please confirm it to finish sign-up', 5, 'ok', 'normal' );
setTimeout( () => {
this.userStore.setUserAuth( true );
this.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
sessionStorage.removeItem( 'redirect' );
}, 5000 );
} else if ( status === 'exists' ) {
this.$refs.notification.cancelNotification( progress );
this.$refs.notification.createNotification( 'An account with this email address already exists. Please log in using it.', 5, 'error', 'normal' );
this.$refs.notification.createNotification( 'If you do not remember your password, reset it!', 5, 'error', 'normal' );
} else {
console.log( status );
}
} );
} );
}
},
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -14,94 +14,94 @@
</template> </template>
<script> <script>
import notifications from '@/components/notifications/notifications.vue'; import notifications from '@/components/notifications/notifications.vue';
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from '@/stores/userStore';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
export default { export default {
name: 'twoFA', name: 'twoFA',
components: { components: {
notifications notifications
}, },
data () { data () {
return { return {
code: { '1': '', '2': '' }, code: { '1': '', '2': '' },
serverPing: null, serverPing: null,
} };
}, },
computed: { computed: {
...mapStores( useUserStore ), ...mapStores( useUserStore ),
}, },
created () { created () {
if ( this.userStore.getUserTwoFACompliant ) { if ( this.userStore.getUserTwoFACompliant ) {
if ( !!window.EventSource ) { if ( window.EventSource ) {
setTimeout( () => { setTimeout( () => {
let startNotification = this.$refs.notification.createNotification( 'Connecting to status service', 20, 'progress', 'normal' ); let startNotification = this.$refs.notification.createNotification( 'Connecting to status service', 20, 'progress', 'normal' );
let source = new EventSource( localStorage.getItem( 'url' ) + '/user/2fa/check', { withCredentials: true } ); let source = new EventSource( localStorage.getItem( 'url' ) + '/user/2fa/check', { withCredentials: true } );
let self = this; let self = this;
source.onmessage = ( e ) => { source.onmessage = ( e ) => {
if ( e.data === 'authenticated' ) { if ( e.data === 'authenticated' ) {
self.userStore.setUserAuth( true ); self.userStore.setUserAuth( true );
self.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' ); self.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
}
} }
};
source.onopen = e => { source.onopen = e => {
self.$refs.notification.createNotification( 'Connected to status service', 5, 'ok', 'normal' ); self.$refs.notification.createNotification( 'Connected to status service', 5, 'ok', 'normal' );
self.$refs.notification.cancelNotification( startNotification );
};
source.addEventListener( 'error', function( e ) {
if ( e.eventPhase == EventSource.CLOSED ) source.close();
if ( e.target.readyState == EventSource.CLOSED ) {
self.$refs.notification.cancelNotification( startNotification ); self.$refs.notification.cancelNotification( startNotification );
}; self.$refs.notification.createNotification( 'Could not connect to status service', 5, 'error', 'normal' );
}
source.addEventListener( 'error', function( e ) { }, false );
if ( e.eventPhase == EventSource.CLOSED ) source.close(); }, 300 );
if ( e.target.readyState == EventSource.CLOSED ) {
self.$refs.notification.cancelNotification( startNotification );
self.$refs.notification.createNotification( 'Could not connect to status service', 5, 'error', 'normal' );
}
}, false)
}, 300 );
} else {
setTimeout( () => {
this.$refs.notification.createNotification( 'Unsupported browser detected. Redirection might take longer to occur!', 20, 'warning', 'normal' );
}, 300 );
// ping server every 5s to check if logged in
this.serverPing = setInterval( () => {
fetch( '/user/2fa/ping' ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
if ( data ) {
if ( data.status === 'ok' ) {
this.userStore.setUserAuth( true );
this.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
}
}
} );
} else {
console.error( 'Request failed' );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
}
} ).catch( error => {
console.error( error );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
} );
}, 5000 );
}
let code = sessionStorage.getItem( '2faCode' ) ? sessionStorage.getItem( '2faCode' ) : '';
this.code = { '1': code.slice( 0, 3 ), '2': code.substring( 3 ) };
} else { } else {
if ( this.userStore.getUserAuthenticated ) { setTimeout( () => {
this.$router.push( '/account' ); this.$refs.notification.createNotification( 'Unsupported browser detected. Redirection might take longer to occur!', 20, 'warning', 'normal' );
} else { }, 300 );
this.$router.push( '/login' ); // ping server every 5s to check if logged in
} this.serverPing = setInterval( () => {
fetch( '/user/2fa/ping' ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
if ( data ) {
if ( data.status === 'ok' ) {
this.userStore.setUserAuth( true );
this.$router.push( sessionStorage.getItem( 'redirect' ) ?? '/account' );
}
}
} );
} else {
console.error( 'Request failed' );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
}
} ).catch( error => {
console.error( error );
this.$refs.notification.createNotification( 'We are sorry, but an error occurred. You will not be redirected automatically', 300, 'error', 'normal' );
} );
}, 5000 );
}
let code = sessionStorage.getItem( '2faCode' ) ? sessionStorage.getItem( '2faCode' ) : '';
this.code = { '1': code.slice( 0, 3 ), '2': code.substring( 3 ) };
} else {
if ( this.userStore.getUserAuthenticated ) {
this.$router.push( '/account' );
} else {
this.$router.push( '/login' );
} }
},
unmounted() {
clearInterval( this.serverPing );
} }
},
unmounted() {
clearInterval( this.serverPing );
} }
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,5 +1,5 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue';
const path = require( 'path' ); const path = require( 'path' );
export default defineConfig( { export default defineConfig( {

View File

@@ -1,11 +0,0 @@
/* eslint-env node */
module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended'
],
parserOptions: {
ecmaVersion: 'latest'
}
}

View File

@@ -0,0 +1,73 @@
/*
* libreevent - .eslintrc.js
*
* Created by Janis Hutz 02/26/2023, Licensed under the GPL V3 License
* https://janishutz.com, development@janishutz.com
*
*
*/
module.exports = {
'env': {
'browser': true,
'commonjs': true,
'es2021': true,
'node': true
},
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended'
],
'overrides': [
],
'parserOptions': {
'ecmaVersion': 'latest'
},
'rules': {
'indent': [
'error',
4
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
],
'spaced-comment': [
'error',
'always'
],
'arrow-spacing': [
'error',
{ 'before': true, 'after': true }
],
'func-call-spacing': [
'error',
'never'
],
'keyword-spacing': [
'error',
{ 'before': true, 'after': true }
],
'key-spacing': [
'error',
{ 'mode': 'strict' }
],
'space-before-blocks': [
'error',
'always'
],
'space-in-parens': [
'error',
'always'
],
'no-var': 'error'
}
};