mirror of
https://github.com/janishutz/libreevent.git
synced 2025-11-25 13:24:24 +00:00
tickets selection + login
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<title>
|
||||
<%= htmlWebpackPlugin.options.title %>
|
||||
</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
<nav>
|
||||
<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>
|
||||
</nav>
|
||||
<router-view />
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition :name="route.meta.transition || 'fade'" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
@@ -38,4 +44,33 @@ nav a {
|
||||
nav a.router-link-exact-active {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
.scale-enter-active,
|
||||
.scale-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.scale-enter-from,
|
||||
.scale-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.4s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.material-symbols-outlined {
|
||||
font-variation-settings:
|
||||
'FILL' 0,
|
||||
'wght' 400,
|
||||
'GRAD' 0,
|
||||
'opsz' 48
|
||||
}
|
||||
</style>
|
||||
|
||||
33
src/webapp/src/components/initial.vue
Normal file
33
src/webapp/src/components/initial.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<img alt="Vue logo" src="../assets/logo.png">
|
||||
<div>
|
||||
<h1>Welcome to myevent!</h1>
|
||||
<p>Let's start the setup by entering the setup key below! You may define a setup key in the config file of myevent. See <a href="https://myevent.janishutz.com/docs/setup/" target="_blank">here</a> for more instructions</p>
|
||||
<form>
|
||||
<label for="key">Your setup key</label><br>
|
||||
<input type="text" v-model="formData[ 'key' ]" required name="key" id="key">
|
||||
</form>
|
||||
<button @click="setup();" class="button">Start setup</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
formData: Object
|
||||
},
|
||||
methods: {
|
||||
setup () {
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
img {
|
||||
width: 20%;
|
||||
}
|
||||
</style>
|
||||
130
src/webapp/src/components/seatplan.vue
Normal file
130
src/webapp/src/components/seatplan.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div class="seatingWrapper">
|
||||
<div class="sidebar">
|
||||
<h2>{{ eventInfo.name }}</h2>
|
||||
<h3>{{ eventInfo.date }}</h3>
|
||||
<h3>{{ eventInfo.location }}</h3>
|
||||
<h3>Selected tickets</h3>
|
||||
<ul v-for="ticket in selectedSeats">
|
||||
<li>{{ seating[ ticket[ 1 ] ][ 'content' ][ ticket[ 0 ] ][ 'name' ] }} {{ eventInfo[ 'categories' ][ seating[ ticket[ 1 ] ][ 'content' ][ ticket[ 0 ] ][ 'category' ] ][ 'price' ] }}</li>
|
||||
</ul>
|
||||
<h3>Total</h3>
|
||||
<router-link to="/cart">To cart</router-link>
|
||||
</div>
|
||||
<div class="seatingPlan">
|
||||
<h3>Seating plan</h3>
|
||||
<p>{{ eventInfo.RoomName }}</p>
|
||||
<div class="seating">
|
||||
<table>
|
||||
<tr v-for="row in seating">
|
||||
<td>
|
||||
{{ row.name }}
|
||||
</td>
|
||||
<td v-for="place in row.content">
|
||||
<div :class="place.category" class="active" v-if="!place.available" @click="selectSeat( place.id, row.id )">
|
||||
<div v-if="place.selected">
|
||||
<span class="material-symbols-outlined">done</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="material-symbols-outlined">living</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="occupied">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let index = sessionStorage.getItem( 'arrayIndex' ) ? parseInt( sessionStorage.getItem( 'arrayIndex' ) ) : 0;
|
||||
|
||||
export default {
|
||||
name: 'noseatplan',
|
||||
props: {
|
||||
ticketID: String
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
seating: { 'r1': { 'name': 'Row 1', 'id': 'r1', 'content':{ 'S1':{ 'name': 'Seat 1', 'id': 'S1', 'available': true, 'selected': false, 'category':'2' } } }, 'r2': { 'name': 'Row 2', 'id': 'r2', 'content':{ 'S1':{ 'name': 'S1', 'id': 'S1', 'available': false, 'selected': false, 'category':'2' } } } },
|
||||
eventInfo: { 'name': 'TestEvent', 'location': 'TestLocation', 'date': 'TestDate', 'RoomName': 'TestRoom', 'currency': 'CHF', 'categories': { '1': { 'price': 20, 'bg': 'black', 'fg': 'white' }, '2': { 'price': 20, 'bg': 'green', 'fg': 'white' } } },
|
||||
selectedSeats: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectSeat( placeID, rowID ) {
|
||||
let data = {};
|
||||
if ( sessionStorage.getItem( 'selectedSeats' ) ) {
|
||||
data = JSON.parse( sessionStorage.getItem( 'selectedSeats' ) );
|
||||
}
|
||||
|
||||
let isDeleting = false;
|
||||
|
||||
for ( let i in data ) {
|
||||
if ( data[ i ][ 0 ] == placeID, data[ i ][ 1 ] == rowID ) {
|
||||
delete data[ i ];
|
||||
isDeleting = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.seating[ rowID ][ 'content' ][ placeID ][ 'selected' ] = !isDeleting;
|
||||
|
||||
if ( !isDeleting ) {
|
||||
data[ index ] = [ placeID, rowID ];
|
||||
index += 1;
|
||||
}
|
||||
sessionStorage.setItem( 'arrayIndex', index );
|
||||
sessionStorage.setItem( 'selectedSeats', JSON.stringify( data ) );
|
||||
this.selectedSeats = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.seatingWrapper {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'main main main sidebar'
|
||||
'main main main sidebar'
|
||||
'main main main sidebar'
|
||||
'main main main sidebar'
|
||||
'main main main sidebar'
|
||||
'main main main sidebar'
|
||||
'main main main sidebar'
|
||||
'main main main sidebar'
|
||||
'main main main sidebar';
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
grid-area: sidebar;
|
||||
background-color: rgb(30, 30, 82);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.seatingPlan {
|
||||
grid-area: main;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: justify;
|
||||
}
|
||||
|
||||
.seating {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.active {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.occupied {
|
||||
background-color: rgb(177, 177, 177);
|
||||
padding: 0.4%;
|
||||
}
|
||||
</style>
|
||||
@@ -34,9 +34,35 @@ const routes = [
|
||||
name: 'adminLogin',
|
||||
component: () => import( '../views/AdminLoginView.vue' ),
|
||||
meta: {
|
||||
title: 'Admin - myevent'
|
||||
title: 'Login :: Admin - myevent'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
name: 'admin',
|
||||
component: () => import( '../views/HomeView.vue' ),
|
||||
meta: {
|
||||
title: 'Admin - myevent'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'settings',
|
||||
name: 'adminSettings',
|
||||
component: () => import( '../views/AdminLoginView.vue' ),
|
||||
meta: {
|
||||
title: 'Admin - myevent'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
name: 'adminMain',
|
||||
component: () => import( '../views/AdminLoginView.vue' ),
|
||||
meta: {
|
||||
title: 'Admin - myevent'
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/signup',
|
||||
name: 'signup',
|
||||
@@ -44,6 +70,33 @@ const routes = [
|
||||
meta: {
|
||||
title: 'Signup - myevent'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/tickets/details',
|
||||
name: 'ticketDetails',
|
||||
component: () => import( '../views/TicketsDetailsView.vue' ),
|
||||
meta: {
|
||||
title: 'Details - myevent',
|
||||
transition: 'scale'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/cart',
|
||||
name: 'cart',
|
||||
component: () => import( '../views/CartView.vue' ),
|
||||
meta: {
|
||||
title: 'Cart - myevent',
|
||||
transition: 'scale'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/purchase',
|
||||
name: 'purchase',
|
||||
component: () => import( '@/views/PurchaseView.vue' ),
|
||||
meta: {
|
||||
title: 'Purchase - myevent',
|
||||
transition: 'scale'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -58,4 +111,13 @@ router.afterEach( ( to, from ) => {
|
||||
document.title = to.meta.title ? to.meta.title : 'default title';
|
||||
} );
|
||||
|
||||
let disallowed = [ 'admin', 'adminMain' ];
|
||||
let isAuthenticated = true;
|
||||
|
||||
router.beforeEach( ( to, from ) => {
|
||||
if ( disallowed.includes( to.name ) && !isAuthenticated ) {
|
||||
return { name: 'adminLogin' };
|
||||
}
|
||||
} );
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<div class="login-app">
|
||||
<h1>Log into your account</h1>
|
||||
<h1>Log into your admin account</h1>
|
||||
<form>
|
||||
<label for="username">Username</label><br>
|
||||
<input type="text" v-model="formData[ 'username' ]" name="username" id="username" required><br><br>
|
||||
<label for="username">Password</label><br>
|
||||
<label for="mail">Email address</label><br>
|
||||
<input type="email" v-model="formData[ 'mail' ]" name="mail" id="mail" required><br><br>
|
||||
<label for="password">Password</label><br>
|
||||
<input type="text" v-model="formData[ 'password' ]" name="password" id="password" required>
|
||||
</form>
|
||||
<button @click="login();" class="button">Log in</button>
|
||||
|
||||
59
src/webapp/src/views/CartView.vue
Normal file
59
src/webapp/src/views/CartView.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="order">
|
||||
<h1>Cart</h1>
|
||||
<h3>Your tickets</h3>
|
||||
<ul>
|
||||
<li>Ticket</li>
|
||||
</ul>
|
||||
<router-link to="/purchase">Purchase now</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.order-app {
|
||||
text-align: justify;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.ticket {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
border-color: black;
|
||||
border-width: 1px;
|
||||
height: fit-content;
|
||||
border-style: solid;
|
||||
padding: 10px;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
.ticket:hover {
|
||||
background-color: rgb(165, 165, 165);
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
.ticket-logo {
|
||||
height: 20vh;
|
||||
width: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.ticket-name {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.ticket-info {
|
||||
margin-left: auto;
|
||||
margin-right: auto
|
||||
}
|
||||
</style>
|
||||
@@ -1,24 +1,23 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<img alt="Vue logo" src="../assets/logo.png">
|
||||
<HelloWorld msg="Welcome to Your Vue.js App" />
|
||||
</div>
|
||||
<initial formData="formData"></initial>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
import HelloWorld from '@/components/HelloWorld.vue'
|
||||
import initial from '@/components/initial.vue';
|
||||
|
||||
export default {
|
||||
name: 'HomeView',
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
}
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
formData: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setup () {
|
||||
|
||||
}
|
||||
},
|
||||
components: {
|
||||
initial
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
img {
|
||||
width: 20%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<div class="login-app">
|
||||
<h1>Log into your account</h1>
|
||||
<h1>Log in</h1>
|
||||
<form>
|
||||
<label for="username">Username</label><br>
|
||||
<input type="text" v-model="formData[ 'username' ]" name="username" id="username" required><br><br>
|
||||
<label for="username">Password</label><br>
|
||||
<input type="text" v-model="formData[ 'password' ]" name="password" id="password" required>
|
||||
<label for="mail">Email</label><br>
|
||||
<input type="email" v-model="formData[ 'mail' ]" name="mail" id="mail" required><br><br>
|
||||
<label for="password">Password</label><br>
|
||||
<input type="password" v-model="formData[ 'password' ]" name="password" id="password" required>
|
||||
</form>
|
||||
<button @click="login();" class="button">Log in</button>
|
||||
<router-link to="/signup" class="button">Sign up instead</router-link>
|
||||
|
||||
@@ -3,7 +3,20 @@
|
||||
<h1>Order tickets</h1>
|
||||
<div class="order-app">
|
||||
<ul>
|
||||
<li>T</li>
|
||||
<li>
|
||||
<router-link to="/tickets/details" class="ticket">
|
||||
<div class="ticket-name">
|
||||
<h3>Event name</h3>
|
||||
<p>Event description</p>
|
||||
</div>
|
||||
<div class="ticket-info">
|
||||
<p>Free seats / max seats</p>
|
||||
<p>Date & time of event</p>
|
||||
<h4>Starting at CHF 20.00</h4>
|
||||
</div>
|
||||
<img src="../assets/logo.png" alt="event logo" class="ticket-logo">
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,16 +31,42 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
li {
|
||||
ul {
|
||||
list-style: none;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.ticket {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
border-color: black;
|
||||
border-width: 1px;
|
||||
height: fit-content;
|
||||
border-style: solid;
|
||||
padding: 10px;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
width: 80%;
|
||||
.ticket:hover {
|
||||
background-color: rgb(165, 165, 165);
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
.ticket-logo {
|
||||
height: 20vh;
|
||||
width: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.ticket-name {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.ticket-info {
|
||||
margin-left: auto;
|
||||
margin-right: auto
|
||||
}
|
||||
</style>
|
||||
|
||||
55
src/webapp/src/views/PurchaseView.vue
Normal file
55
src/webapp/src/views/PurchaseView.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="order">
|
||||
<h1>Purchase</h1>
|
||||
<h3>Please choose a payment option</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.order-app {
|
||||
text-align: justify;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.ticket {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
border-color: black;
|
||||
border-width: 1px;
|
||||
height: fit-content;
|
||||
border-style: solid;
|
||||
padding: 10px;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
.ticket:hover {
|
||||
background-color: rgb(165, 165, 165);
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
.ticket-logo {
|
||||
height: 20vh;
|
||||
width: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.ticket-name {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.ticket-info {
|
||||
margin-left: auto;
|
||||
margin-right: auto
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +1,19 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<div class="login-app">
|
||||
<h1>Log into your account</h1>
|
||||
<h1>Sign up</h1>
|
||||
<form>
|
||||
<label for="username">Username</label><br>
|
||||
<input type="text" v-model="formData[ 'username' ]" name="username" id="username" required><br><br>
|
||||
<label for="username">Password</label><br>
|
||||
<input type="text" v-model="formData[ 'password' ]" name="password" id="password" required>
|
||||
<label for="mail">Email</label><br>
|
||||
<input type="email" v-model="formData[ 'mail' ]" name="mail" id="mail" required><br><br>
|
||||
<label for="name">Full name</label><br>
|
||||
<input type="text" v-model="formData[ 'name' ]" name="name" id="name" required><br><br>
|
||||
<label for="password">Password</label><br>
|
||||
<input type="password" v-model="formData[ 'password' ]" name="password" id="password" required><br><br>
|
||||
<label for="password2">Confirm password</label><br>
|
||||
<input type="password" v-model="formData[ 'password2' ]" name="password2" id="password2" required>
|
||||
</form>
|
||||
<button @click="login();" class="button">Log in</button>
|
||||
<router-link to="/signup" class="button">Sign up instead</router-link>
|
||||
<button @click="signup();" class="button">Sign up</button>
|
||||
<router-link to="/login" class="button">Log in instead</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -22,7 +26,7 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
login () {
|
||||
signup () {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
24
src/webapp/src/views/TicketsDetailsView.vue
Normal file
24
src/webapp/src/views/TicketsDetailsView.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="details">
|
||||
<!-- Load correct component depending on what the event's config is -->
|
||||
<seatplan ticketID="haoag"></seatplan>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import seatplan from '@/components/seatplan.vue';
|
||||
|
||||
export default {
|
||||
name: 'TicketsDetailsView',
|
||||
components: {
|
||||
seatplan
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user