lots of progress

This commit is contained in:
2023-07-01 15:11:58 +02:00
parent a06a3ade22
commit fc030bd56e
14 changed files with 469 additions and 62 deletions

View File

@@ -1,8 +1,14 @@
# Account view:
- Load: email, settings, change pw, change mail, see all tickets
- set page title based on settings
- make pricing groups changeable in UI (event categories)
- Fix text field overflow (text too big for box)
- Other optimisation for seat plan editor
- Implement Permission system
- add webpack to project website to decrease file size

View File

@@ -12,7 +12,7 @@
<router-link to="/">Home</router-link> |
<router-link to="/tickets">Tickets</router-link> |
<router-link to="/cart">Cart</router-link> |
<router-link to="/login">Account</router-link> |
<router-link to="/account">Account</router-link> |
<button @click="changeTheme();" v-html="theme" id="themeSelector"></button>
</nav>
<router-view v-slot="{ Component, route }">
@@ -31,7 +31,7 @@
--popup-color: rgb(224, 224, 224);
--accent-color: #42b983;
--hover-color: rgb(165, 165, 165);
--accent-background-hover: #4380a8;
--accent-background-hover: rgb(124, 140, 236);
--overlay-color: rgba(0, 0, 0, 0.7);
--inactive-color: rgb(100, 100, 100);
--highlight-backdrop: rgb(143, 134, 192);

View File

@@ -30,20 +30,6 @@
</div>
</div>
</div>
<div class="overlay" id="placeNotAvailable">
<div class="popup">
<div class="popup-content">
<div class="close-container">
<span class="material-symbols-outlined close-button" @click="closePlaceNotAvailablePopup()">close</span>
</div>
<div class="popup-content-wrapper">
<h3>One or more seat(s) you have previously selected is/are no longer available!</h3>
<p>Please select another one!</p>
<button class="button" @click="closePlaceNotAvailablePopup()">Ok</button>
</div>
</div>
</div>
</div>
</div>
</template>
@@ -86,10 +72,7 @@ export default {
}
if ( showError ) {
setTimeout( function () {
$( '#placeNotAvailable' ).show( 200 );
console.log( 'showing error message' );
}, 500 );
// TODO: Show popup that no more tickets in a category are available
}
@@ -133,9 +116,6 @@ export default {
sessionStorage.setItem( 'cart', JSON.stringify( cart ) );
}
},
closePlaceNotAvailablePopup () {
$( '#placeNotAvailable' ).hide( 300 );
},
selectSeat( placeID, rowID ) {
/*
This function allows the user to select a seat and deselect it, if it has previously

View File

@@ -209,7 +209,7 @@ export default {
data () {
return {
internal: {},
categories: {},
categories: { '1':{} },
}
},
methods: {
@@ -219,7 +219,7 @@ export default {
this.internal[ value ] = {};
for ( let info in this.draggables[ value ] ) {
if ( info === 'w' || info === 'h' || info === 'x' || info === 'y' ) {
this.internal[ value ][ info ] = Math.round( this.draggables[ value ][ info ] / this.scaleFactor );
this.internal[ value ][ info ] = Math.round( ( this.draggables[ value ][ info ] / this.scaleFactor ) * 1000 ) / 1000;
} else {
this.internal[ value ][ info ] = this.draggables[ value ][ info ];
}

View File

@@ -11,14 +11,14 @@
<div id="window">
<!-- TODO: Add additional div with v-if to check if a location has been selected and warn if not so. -->
<properties class="properties" v-model:draggables="draggables" @updated="handleUpdate" :scale-factor="scaleFactor" :active="active" :history-pos="historyPos" :zoom-factor="zoomFactor" v-model:general-settings="generalSettings"></properties>
<div class="parent">
<div class="parent" id="parent">
<div class="content-parent">
<Vue3DraggableResizable v-for="draggable in draggables" :initW="draggable.w" :initH="draggable.h" v-model:x="draggable.x" v-model:y="draggable.y" v-model:w="draggable.w" v-model:h="draggable.h"
v-model:active="draggable.active" v-model:draggable="draggable.draggable" :resizable="draggable.resizable" :parent="true" @activated="activateComponent( draggable.id );"
@drag-end="saveHistory();" @resize-end="saveHistory();" @contextmenu="( e ) => { e.preventDefault(); }" class="draggable-box">
<circularSeatplanComponent v-if="draggable.shape == 'circular' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin" :starting-row="draggable.startingRow"></circularSeatplanComponent>
<trapezoidSeatplanComponent v-else-if="draggable.shape == 'trapezoid' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin" :starting-row="draggable.startingRow"></trapezoidSeatplanComponent>
<rectangularSeatplanComponent v-else-if="draggable.shape == 'rectangular' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin"></rectangularSeatplanComponent>
<circularSeatplanComponent v-if="draggable.shape == 'circular' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin" :starting-row="draggable.startingRow" :id="draggable.id" @seatingInfo="( info ) => { handleSeatCountInfo( info ); }"></circularSeatplanComponent>
<trapezoidSeatplanComponent v-else-if="draggable.shape == 'trapezoid' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin" :starting-row="draggable.startingRow" :id="draggable.id" @seatingInfo="( info ) => { handleSeatCountInfo( info ); }"></trapezoidSeatplanComponent>
<rectangularSeatplanComponent v-else-if="draggable.shape == 'rectangular' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin" :id="draggable.id" @seatingInfo="( info ) => { handleSeatCountInfo( info ); }"></rectangularSeatplanComponent>
<stagesSeatplanComponent v-else-if="draggable.type == 'stage'" :origin="draggable.origin" :shape="draggable.shape"></stagesSeatplanComponent>
<standingSeatplanComponent v-else-if="draggable.type == 'stand'" :origin="draggable.origin" :shape="draggable.shape"></standingSeatplanComponent>
<textFieldSeatplanComponent v-else-if="draggable.type == 'text'" :text="draggable.text.text" :text-size="draggable.text.textSize" :colour="draggable.text.colour" :origin="draggable.origin" :scale-factor="scaleFactor"></textFieldSeatplanComponent>
@@ -79,6 +79,7 @@
zoomFactor: 1,
historyPos: 0,
generalSettings: { 'namingScheme': 'numeric' },
seatCountInfo: { 'data': {}, 'count': 0 },
}
},
methods: {
@@ -184,7 +185,7 @@
returnArray[ entry ] = {};
for ( let attributes in valueArray[ entry ] ) {
if ( allowedAttributes.includes( attributes ) ) {
returnArray[ entry ][ attributes ] = Math.round( valueArray[ entry ][ attributes ] / this.scaleFactor );
returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] / this.scaleFactor ) * 1000 ) / 1000;
} else {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
}
@@ -199,7 +200,7 @@
returnArray[ entry ] = {};
for ( let attributes in valueArray[ entry ] ) {
if ( allowedAttributes.includes( attributes ) ) {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ] * this.scaleFactor;
returnArray[ entry ][ attributes ] = Math.round( ( valueArray[ entry ][ attributes ] * this.scaleFactor ) * 1000 ) / 1000;
} else {
returnArray[ entry ][ attributes ] = valueArray[ entry ][ attributes ];
}
@@ -262,6 +263,7 @@
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) );
},
saveDraft () {
// TODO: Save seat count and seat config to server as well
let progressNotification = this.$refs.notification.createNotification( 'Saving as draft', 5, 'progress', 'normal' );
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) );
this.$refs.notification.createNotification( 'Saved as draft', 5, 'ok', 'normal' );
@@ -275,6 +277,8 @@
addNewElement () {
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, 'seatCountingStartingPoint': 0, 'sector': 'A', 'text': { 'text': 'TestText', 'textSize': 20, 'colour': '#20FFFF' }, 'ticketCount': 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 () {
@@ -314,8 +318,11 @@
this.loadSeatplan();
}
},
handleSeatCountInfo ( info ) {
this.seatCountInfo[ 'data' ][ info.id ] = info.data;
},
getSeatCount () {
console.log( 'Seat count is: ' + document.getElementsByClassName( 'seats' ).length );
this.seatCountInfo[ 'count' ] = document.getElementsByClassName( 'seats' ).length;
},
},
created () {

View File

@@ -1,17 +0,0 @@
<template>
<div>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -44,6 +44,10 @@ export default {
startingRow: {
type: Number,
"default": 1,
},
id: {
type: Number,
"default": 1
}
},
data () {
@@ -60,8 +64,10 @@ export default {
const size = 33;
let count = Math.min( Math.floor( w / size ), Math.floor( h / size ) );
this.seats = {};
let details = { 'data': {}, 'id': this.id };
for ( let row = this.startingRow; row < count; row++ ) {
let nn = row * ( Math.PI / 2 );
details.data[ row ] = Math.floor( nn );
let r = row * size;
this.seats[ row ] = {};
for ( let n = 0; n < nn; n++ ) {
@@ -77,6 +83,7 @@ export default {
}
}
}
this.$emit( 'seatingInfo', details );
},
setScaleFactor () {
for ( let row in this.seats ) {

View File

@@ -41,6 +41,10 @@ export default {
type: Number,
"default": 1,
},
id: {
type: Number,
"default": 1
}
},
data () {
return {
@@ -55,6 +59,7 @@ export default {
let h = Math.floor( this.h / this.scaleFactor );
const size = 33;
this.seats = {};
let details = { 'data': { '0': Math.floor( w / size ) }, 'id': this.id };
for ( let row = 0; row < Math.floor( h / size ); row++ ) {
this.seats[ row ] = {};
for ( let n = 0; n < Math.floor( w / size ); n++ ) {
@@ -69,6 +74,7 @@ export default {
}
}
}
this.$emit( 'seatingInfo', details );
},
setScaleFactor () {
for ( let row in this.seats ) {

View File

@@ -44,6 +44,10 @@ export default {
startingRow: {
type: Number,
"default": 1,
},
id: {
type: Number,
"default": 1
}
},
data () {
@@ -64,9 +68,11 @@ export default {
let count = Math.floor( heightTriangle / ( sideOffset * 2 ) );
const angle = Math.PI / 4;
this.seats = {};
let details = { 'data': {}, 'id': this.id };
for ( let row = this.startingRow; row < count; row++ ) {
let nn = 2 + ( row - 1 ) * 2;
this.seats[ row ] = {};
details.data[ row ] = Math.floor( nn );
for ( let n = 0; n < nn; n++ ) {
let side = n * sideOffset;
if ( this.origin === 1 ) {
@@ -80,6 +86,7 @@ export default {
}
}
}
this.$emit( 'seatingInfo', details );
},
setScaleFactor () {
for ( let row in this.seats ) {

View File

@@ -0,0 +1,251 @@
<!--
* libreevent - window.vue
*
* Created by Janis Hutz 05/12/2023, Licensed under the GPL V3 License
* https://janishutz.com, development@janishutz.com
*
*
-->
<template>
<div id="window">
<!-- TODO: Add additional div with v-if to check if a location has been selected and warn if not so. -->
<div class="parent" id="parent">
<div class="content-parent">
<Vue3DraggableResizable v-for="draggable in draggables" :initW="draggable.w" :initH="draggable.h" :x="draggable.x" :y="draggable.y" :w="draggable.w" :h="draggable.h"
:active="false" :draggable="false" :resizable="false" :parent="true" @contextmenu="( e ) => { e.preventDefault(); }" class="draggable-box">
<circularSeatplanComponent v-if="draggable.shape == 'circular' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin" :starting-row="draggable.startingRow"></circularSeatplanComponent>
<trapezoidSeatplanComponent v-else-if="draggable.shape == 'trapezoid' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin" :starting-row="draggable.startingRow"></trapezoidSeatplanComponent>
<rectangularSeatplanComponent v-else-if="draggable.shape == 'rectangular' && draggable.type == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin"></rectangularSeatplanComponent>
<stagesSeatplanComponent v-else-if="draggable.type == 'stage'" :origin="draggable.origin" :shape="draggable.shape"></stagesSeatplanComponent>
<standingSeatplanComponent v-else-if="draggable.type == 'stand'" :origin="draggable.origin" :shape="draggable.shape"></standingSeatplanComponent>
<textFieldSeatplanComponent v-else-if="draggable.type == 'text'" :text="draggable.text.text" :text-size="draggable.text.textSize" :colour="draggable.text.colour" :origin="draggable.origin" :scale-factor="scaleFactor"></textFieldSeatplanComponent>
</Vue3DraggableResizable>
</div>
</div>
<notifications ref="notification" location="topleft"></notifications>
</div>
</template>
<script>
import Vue3DraggableResizable from 'vue3-draggable-resizable';
import circularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/circular.vue';
import rectangularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/rectangular.vue';
import trapezoidSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/trapezoid.vue';
import stagesSeatplanComponent from '@/components/seatplan/seatplanComponents/stages.vue';
import standingSeatplanComponent from '@/components/seatplan/seatplanComponents/standing.vue';
import textFieldSeatplanComponent from '@/components/seatplan/seatplanComponents/textField.vue';
import notifications from '@/components/notifications/notifications.vue';
import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css';
export default {
'name': 'window',
components: {
Vue3DraggableResizable,
circularSeatplanComponent,
rectangularSeatplanComponent,
trapezoidSeatplanComponent,
stagesSeatplanComponent,
standingSeatplanComponent,
textFieldSeatplanComponent,
notifications,
},
data() {
return {
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, 'seatCountingStartingPoint': 1, 'sector': 'A', 'text': { 'text': 'TestText', 'textSize': 20, 'colour': '#20FFFF' } }, 'ticketCount': 1 },
available: { 'redo': false, 'undo': false },
scaleFactor: 1,
sizePoll: null,
prevSize: { 'h': window.innerHeight, 'w': window.innerWidth },
zoomFactor: 1,
historyPos: 0,
generalSettings: { 'namingScheme': 'numeric' },
}
},
methods: {
/*
Coords are from top left corner of box.
The below function is executed as the init hook (created hook)
of vue.js, so whenever this particular page is loaded.
It loads previous data (if available) and starts the event listeners
for keyevents (like delete) and also check if the user uses a desktop
browser that meets all the requirements for being able to use the editor
reliably according to testing done.
*/
runHook () {
let self = this;
this.zoomFactor = sessionStorage.getItem( 'zoom' ) ? parseFloat( sessionStorage.getItem( 'zoom' ) ) : 1;
/*
Keybinds:
- Delete: delete selected object
- Ctrl + S: Save
- Ctrl + Z: Undo
- Ctrl + Y: Redo
*/
document.onkeydown = function ( event ) {
if ( event.key === 'Delete' ) {
event.preventDefault();
self.deleteSelected();
} else if ( event.ctrlKey && event.key === 's' ) {
event.preventDefault();
self.saveDraft();
} else if ( ( event.ctrlKey && event.key === 'y' ) ) {
event.preventDefault();
self.historyOp( 'redo' );
} else if ( event.ctrlKey && event.key === 'z' ) {
event.preventDefault();
self.historyOp( 'undo' );
} else if ( event.ctrlKey && event.key === 'i' ) {
event.preventDefault();
self.addNewElement();
} else if ( event.key === '+' ) {
self.zoom( 0.2 );
} else if ( event.key === '-' ) {
self.zoom( -0.2 );
} else if ( event.key === '=' ) {
self.zoom( 1 );
}
};
this.loadSeatplan();
// TODO: Add warning for untested browsers & suboptimal window sizes!
},
eventHandler ( e ) {
if ( this.prevSize.h != window.innerHeight || this.prevSize.w != window.innerWidth ) {
this.prevSize = { 'h': window.innerHeight, 'w': window.innerWidth };
this.loadSeatplan();
}
},
loadSeatplan () {
/*
Calculate scale factor (this adds support for differently sized screens)
900px is the "default" height
*/
let height = $( document ).height() * 0.8;
this.scaleFactor = ( height / 900 ) * this.zoomFactor;
/*
Load seatplan
*/
// TODO: load from server
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;
}
}
},
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;
},
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 {
this.$refs.notification.createNotification( 'Maximum zoom factor reached', 5, 'warning', 'normal' );
}
} else {
this.zoomFactor += scale;
}
sessionStorage.setItem( 'zoom', this.zoomFactor );
this.loadSeatplan();
}
},
},
created () {
this.runHook();
this.sizePoll = setInterval( this.eventHandler, 250 );
},
unmounted() {
clearInterval( this.sizePoll );
},
}
</script>
<style scoped>
.parent {
height: 90vh;
aspect-ratio: 16 / 9;
top: 7.5vh;
left: 3vw;
position: absolute;
border: black 1px solid;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
overflow: scroll;
}
.draggable-box {
cursor: all-scroll;
}
.properties {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
z-index: 1;
background-color: var( --accent-background );
color: var( --secondary-color );
width: 20vw;
height: 90vh;
top: 7.5vh;
right: 0.5vw;
overflow: scroll;
}
.content-parent {
aspect-ratio: 16 / 9;
height: 400%;
}
.toolbar {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: absolute;
top: 7.5vh;
left: 0.5vw;
height: 90vh;
}
.toolbar button {
margin-top: 10%;
cursor: pointer;
}
.toolbar button:disabled {
cursor: default;
}
</style>

View File

@@ -10,7 +10,7 @@
import { defineStore } from "pinia";
export const useUserStore = defineStore ( 'user', {
state: () => ( { 'isUserAuth': true, 'isAdminAuth': true, 'userPermissions': {} } ),
state: () => ( { 'isUserAuth': true, 'isAdminAuth': true, 'userData': {} } ),
getters: {
getUserAuthenticated: ( state ) => state.isUserAuth,
getAdminAuthenticated: ( state ) => state.isAdminAuth,

View File

@@ -16,13 +16,13 @@
<nav class="side-nav">
<div class="side-nav-wrapper">
<img src="@/assets/logo.png" alt="libreevent logo" style="width: 80%; margin-left: 10%; margin-bottom: 5%;">
<router-link to="/admin" class="admin-menu" @click="navMenu( 'hide' )">Home</router-link>
<router-link to="/admin/pages" class="admin-menu" @click="navMenu( 'hide' )">Pages</router-link>
<router-link to="/admin/events" class="admin-menu" @click="navMenu( 'hide' )">Events</router-link>
<router-link to="/admin/locations" class="admin-menu" @click="navMenu( 'hide' )">Locations</router-link>
<router-link to="/admin/plugins" class="admin-menu" @click="navMenu( 'hide' )">Plugins</router-link>
<router-link to="/admin/settings" class="admin-menu" @click="navMenu( 'hide' )">Settings</router-link>
<button to="/admin/login" class="admin-menu" @click="logout()">Logout</button>
<router-link to="/admin" class="admin-menu" @click="navMenu( 'hide' )" title="The home page of the admin panel">Home</router-link>
<router-link to="/admin/pages" class="admin-menu" @click="navMenu( 'hide' )" title="Modify your landing page, your terms of service, etc.">Pages</router-link>
<router-link to="/admin/events" class="admin-menu" @click="navMenu( 'hide' )" title="Change, view and analyse everything about your events">Events</router-link>
<router-link to="/admin/locations" class="admin-menu" @click="navMenu( 'hide' )" title="Change settings about your event locations">Locations</router-link>
<router-link to="/admin/plugins" class="admin-menu" @click="navMenu( 'hide' )" title="Install, Uninstall and manage plugins">Plugins</router-link>
<router-link to="/admin/settings" class="admin-menu" @click="navMenu( 'hide' )" title="Change global settings for libreevent">Settings</router-link>
<button to="/admin/login" class="admin-menu" @click="logout()" title="Log out of the admin panel">Logout</button>
</div>
</nav>
<div class="backdrop" @click="navMenu( 'hide' )"></div>

View File

@@ -11,6 +11,7 @@
<div>
<h2>Locations</h2>
<p>Here you can change everything regarding event locations. All locations can have a seating plan.</p>
<button @click="addLocation();">Add new location</button>
<div class="location-app" v-if="Object.keys( locations ).length">
<ul>
<li v-for="location in locations">
@@ -28,6 +29,7 @@
No locations configured, please <b @click="addLocation();" style="cursor: pointer;">add</b> one
</div>
<popups ref="popup" size="big"></popups>
<popups ref="popup2" size="huge"></popups>
</div>
</template>
@@ -75,7 +77,33 @@
, 'settings' );
},
addLocation () {
this.$refs.popup.openPopup( 'Add a new location', {
'locationName': {
'display': 'Location name',
'id': 'locationName',
'tooltip':'Give the location the event takes place a name. This name will also be shown to customers',
'value': '',
'type': 'text',
},
'usesSeatplan': {
'display': 'Use seat plan?',
'id': 'usesSeatplan',
'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' );
},
}
};

View File

@@ -11,19 +11,37 @@
<div>
<h2>Settings</h2>
<settings v-model:settings="settings"></settings>
<p>Detailed explanation of payment gateways can be found <a href="https://libreevent.janishutz.com/docs/payments" target="_blank">here</a>. You may install more payment gateway integrations in the plugins section.</p>
<div class="admin-settings">
<h2>Admin Accounts</h2>
<p>Before setting or editing permissions here, please read the corresponding section of the documentation <a href="https://libreevent.janishutz.com/docs/admin-panel/settings/admin-accounts#permissions" target="_blank">here</a>.
<br>Usually, the permissions automatically set by the system on account creation should be appropriate.</p>
<div v-for="account in adminAccounts" class="account" @click="showAccountSettings( account.username );" title="Edit permissions of this account">
<div class="location-name">
<h3>{{ account.username }}</h3>
<p>{{ account.email }}</p>
</div>
</div>
</div>
<popups ref="popup" size="big"></popups>
</div>
</template>
<script>
import settings from '@/components/settings/settings.vue';
import popups from '@/components/notifications/popups.vue';
export default {
name: 'adminSettingsView',
components: {
settings,
popups,
},
data () {
return {
adminAccounts: { 'janis': { 'username': 'janis', 'email': 'info@janishutz.com', 'permissions': [ ] }, 'admin': { 'username': 'admin', 'email': 'development@janishutz.com', 'permissions': [ ] } },
settings: {
'2fa': {
'display': 'Require Two-Factor-Authentication of user',
@@ -45,9 +63,123 @@
'value': 'never'
},
}
},
'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 address?',
'id': 'phoneNumberRequired',
'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',
},
'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': {
'stripe': {
'displayName':'Stripe',
'value': 'stripe'
},
'adyen': {
'displayName':'Adyen',
'value': 'adyen'
},
}
},
}
}
},
methods: {
showAccountSettings ( account ) {
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',
},
'entryControl': {
'display': 'Entry control',
'id': 'entryControl',
'tooltip':'Change this setting to allow or disallow the selected user to execute entry control at the entrance to your event location.',
'value': true,
'type': 'toggle',
},
}
, 'settings' );
}
}
};
// TODO: Load gateways and settings in general from server.
</script>
<style scoped>
.admin-settings {
text-align: justify;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.account {
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
color: var( --primary-color );
border-color: var( --primary-color );
border-width: 1px;
height: fit-content;
border-style: solid;
padding: 10px;
transition: 0.4s;
cursor: pointer;
width: 60%;
margin-bottom: 5px;
}
.account:hover {
background-color: var( --hover-color );
transition: 0.4s;
}
.location-name {
margin-right: auto;
max-width: 35%;
}
</style>