Move software files one directory up, Readme
This commit is contained in:
100
src/pages/account/accountDataPage/accountDataCard.vue
Normal file
100
src/pages/account/accountDataPage/accountDataCard.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import { useFeedbackStore } from '@/stores/feedback.store';
|
||||
|
||||
const accountStore = useAccountStore()
|
||||
const feedbackStore = useFeedbackStore()
|
||||
|
||||
const passwordRules = [
|
||||
value => {
|
||||
if (value) {
|
||||
return true
|
||||
} else {
|
||||
return feedbackStore.i18n.t('misc.validation.required')
|
||||
}
|
||||
},
|
||||
value => {
|
||||
if (value?.length >= 8) {
|
||||
return true
|
||||
} else {
|
||||
return feedbackStore.i18n.t('misc.validation.notEnoughChars')
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const stringRules = [
|
||||
value => {
|
||||
if (value) {
|
||||
return true
|
||||
} else {
|
||||
return feedbackStore.i18n.t('misc.validation.required')
|
||||
}
|
||||
},
|
||||
value => {
|
||||
if (/[^0-9]/.test(value)) {
|
||||
return true
|
||||
} else {
|
||||
return feedbackStore.i18n.t('misc.validation.noDigitsAllowed')
|
||||
}
|
||||
},
|
||||
value => {
|
||||
if (value?.length >= 3) {
|
||||
return true
|
||||
} else {
|
||||
return feedbackStore.i18n.t('misc.validation.notEnoughChars')
|
||||
}
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('account.masterData')"
|
||||
icon="mdi-account"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.email')"
|
||||
v-model="accountStore.userAccount.email"
|
||||
disabled
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.username')"
|
||||
v-model="accountStore.userAccount.username"
|
||||
disabled
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.password')"
|
||||
v-model="accountStore.userAccount.password"
|
||||
type="password"
|
||||
:rules="passwordRules"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.firstName')"
|
||||
v-model="accountStore.userAccount.firstName"
|
||||
:rules="stringRules"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.lastName')"
|
||||
v-model="accountStore.userAccount.lastName"
|
||||
:rules="stringRules"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</card-view>
|
||||
</template>
|
||||
48
src/pages/account/accountDataPage/accountManagingCard.vue
Normal file
48
src/pages/account/accountDataPage/accountManagingCard.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import confirmDialog from '@/components/basics/confirmDialog.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const showConfirmDialog = ref(false)
|
||||
const accountStore = useAccountStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('account.accountManagement')"
|
||||
icon="mdi-account-edit"
|
||||
>
|
||||
<v-row>
|
||||
<v-col class="d-flex justify-center align-center">
|
||||
<outlined-button
|
||||
prepend-icon="mdi-delete"
|
||||
color="red"
|
||||
:loading="accountStore.fetchInProgress"
|
||||
@click="showConfirmDialog = true"
|
||||
>
|
||||
{{ $t("account.deleteAccount.deleteAccount") }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
|
||||
<v-col class="d-flex justify-center align-center">
|
||||
<outlined-button
|
||||
prepend-icon="mdi-content-save"
|
||||
color="green"
|
||||
:loading="accountStore.fetchInProgress"
|
||||
@click="accountStore.updateAccount()"
|
||||
>
|
||||
{{ $t("misc.actions.save") }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</card-view>
|
||||
|
||||
<confirm-dialog
|
||||
v-model="showConfirmDialog"
|
||||
:title="$t('account.deleteAccount.dialog.title')"
|
||||
:description="$t('account.deleteAccount.dialog.description')"
|
||||
:on-confirm="() => accountStore.deleteAccount(accountStore.userAccount)"
|
||||
/>
|
||||
</template>
|
||||
95
src/pages/account/accountDataPage/addressesCard.vue
Normal file
95
src/pages/account/accountDataPage/addressesCard.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { AddressModel } from '@/data/models/user/addressModel';
|
||||
import { getNumberStartRules, getPostalRules, getStringRules } from '@/scripts/validationRules';
|
||||
|
||||
const accountStore = useAccountStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
icon="mdi-home"
|
||||
:title="$t('account.userData.address')"
|
||||
>
|
||||
<v-expansion-panels v-if="accountStore.userAccount.addresses.length > 0">
|
||||
<v-expansion-panel
|
||||
v-for="address in accountStore.userAccount.addresses"
|
||||
color="primary"
|
||||
>
|
||||
<template #title>
|
||||
{{ address.street + ' ' + address.houseNumber }}
|
||||
</template>
|
||||
|
||||
<template #text>
|
||||
<v-row class="pt-5">
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.street')"
|
||||
v-model="address.street"
|
||||
:rules="getStringRules()"
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.houseNumber')"
|
||||
v-model="address.houseNumber"
|
||||
:rules="getNumberStartRules()"
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.postalCode')"
|
||||
v-model="address.postalCode"
|
||||
:rules="getPostalRules()"
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.placeOfResidence')"
|
||||
v-model="address.city"
|
||||
:rules="getStringRules()"
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col class="d-flex justify-center align-center">
|
||||
<outlined-button
|
||||
@click="accountStore.removeAddress(address)"
|
||||
color="red"
|
||||
prepend-icon="mdi-delete"
|
||||
>
|
||||
{{ $t('misc.actions.remove') }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
|
||||
<v-empty-state
|
||||
v-else
|
||||
:title="$t('account.noAddresses')"
|
||||
icon="mdi-home-off"
|
||||
/>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
@click="accountStore.userAccount.addresses.push(new AddressModel())"
|
||||
prepend-icon="mdi-plus"
|
||||
color="green"
|
||||
>
|
||||
{{ $t('misc.actions.add') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</card-view>
|
||||
</template>
|
||||
38
src/pages/account/accountDataPage/index.vue
Normal file
38
src/pages/account/accountDataPage/index.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import accountDataCard from './accountDataCard.vue';
|
||||
import accountManagingCard from './accountManagingCard.vue';
|
||||
import addressesCard from './addressesCard.vue';
|
||||
import paymentsCard from './paymentsCard.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import accountSubPageLayout from '@/layouts/accountSubPageLayout.vue';
|
||||
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<account-sub-page-layout>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<account-data-card />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<addresses-card />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<payments-card />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<account-managing-card />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</account-sub-page-layout>
|
||||
</template>
|
||||
93
src/pages/account/accountDataPage/paymentsCard.vue
Normal file
93
src/pages/account/accountDataPage/paymentsCard.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { PaymentModel } from '@/data/models/user/paymentModel';
|
||||
import { getIbanRules, getStringRules } from '@/scripts/validationRules';
|
||||
import cardViewOneLine from '@/components/basics/cardViewOneLine.vue';
|
||||
|
||||
const accountStore = useAccountStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
icon="mdi-currency-usd"
|
||||
:title="$t('account.userData.payment')"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<card-view-one-line
|
||||
color="amber"
|
||||
prepend-icon="mdi-alert"
|
||||
:title="$t('account.noRealPaymentsNeeded')"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-if="accountStore.userAccount.payments.length > 0">
|
||||
<v-col>
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel
|
||||
v-for="payment in accountStore.userAccount.payments"
|
||||
color="primary"
|
||||
>
|
||||
<template #title>
|
||||
{{ payment.bankName }}
|
||||
</template>
|
||||
<template #text>
|
||||
<v-row class="pt-5">
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.bankName')"
|
||||
v-model="payment.bankName"
|
||||
:rules="getStringRules()"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.iban')"
|
||||
v-model="payment.iban"
|
||||
:rules="getIbanRules()"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col class="d-flex justify-center align-center">
|
||||
<outlined-button
|
||||
@click="accountStore.removePayment(payment)"
|
||||
color="red"
|
||||
prepend-icon="mdi-delete"
|
||||
>
|
||||
{{ $t('misc.actions.remove') }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-else>
|
||||
<v-col>
|
||||
<v-empty-state
|
||||
:title="$t('account.noPayments')"
|
||||
icon="mdi-currency-usd-off"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
@click="accountStore.userAccount.payments.push(new PaymentModel())"
|
||||
prepend-icon="mdi-plus"
|
||||
color="green"
|
||||
>
|
||||
{{ $t('misc.actions.add') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</card-view>
|
||||
</template>
|
||||
60
src/pages/account/accountHomePage/index.vue
Normal file
60
src/pages/account/accountHomePage/index.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const accountStore = useAccountStore()
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container max-width="1000">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<card-view
|
||||
:title="$t('misc.greeting', { msg: accountStore.userAccount.username })"
|
||||
icon="mdi-hand-wave"
|
||||
>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<card-view
|
||||
:title="$t('order.order', 2)"
|
||||
icon="mdi-basket-check"
|
||||
@click="router.push('/account/orders')"
|
||||
>
|
||||
{{ $t('order.ordersDescription') }}
|
||||
</card-view>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<card-view
|
||||
:title="$t('account.accountManagement')"
|
||||
icon="mdi-account"
|
||||
@click="router.push('/account/data')"
|
||||
>
|
||||
{{ $t('account.accountManagementDescription') }}
|
||||
</card-view>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<card-view
|
||||
:title="$t('account.logout.logout')"
|
||||
icon="mdi-logout"
|
||||
@click="accountStore.logout(); router.push('/account/login')"
|
||||
>
|
||||
{{ $t('account.logout.logoutDescription') }}
|
||||
</card-view>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</card-view>
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
27
src/pages/account/loginPage/index.vue
Normal file
27
src/pages/account/loginPage/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import loginForm from './loginForm.vue';
|
||||
import registerForm from './registerForm.vue';
|
||||
|
||||
const showRegisterCard = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container max-width="500">
|
||||
<v-expand-transition>
|
||||
<v-row v-if="!showRegisterCard">
|
||||
<v-col>
|
||||
<login-form v-model:show-register-card="showRegisterCard" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-expand-transition>
|
||||
|
||||
<v-expand-transition>
|
||||
<v-row v-if="showRegisterCard">
|
||||
<v-col>
|
||||
<register-form v-model:show-register-card="showRegisterCard" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-expand-transition>
|
||||
</v-container>
|
||||
</template>
|
||||
73
src/pages/account/loginPage/loginForm.vue
Normal file
73
src/pages/account/loginPage/loginForm.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const accountStore = useAccountStore()
|
||||
const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false })
|
||||
const router = useRouter()
|
||||
|
||||
async function startLogin() {
|
||||
accountStore.login()
|
||||
.then(result => {
|
||||
if (accountStore.userAccountToken != "") {
|
||||
router.push("/account/home")
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('account.login.login')"
|
||||
icon="mdi-login"
|
||||
max-width="600"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.username')"
|
||||
prepend-icon="mdi-account"
|
||||
v-model="accountStore.loginData.username"
|
||||
variant="outlined"
|
||||
clearable
|
||||
@keyup.enter="startLogin"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.password')"
|
||||
prepend-icon="mdi-key"
|
||||
type="password"
|
||||
variant="outlined"
|
||||
v-model="accountStore.loginData.password"
|
||||
clearable
|
||||
@keyup.enter="startLogin"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
@click="showRegisterCard = true"
|
||||
prepend-icon="mdi-plus"
|
||||
:loading="accountStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('account.register') }}
|
||||
</outlined-button>
|
||||
|
||||
<outlined-button
|
||||
append-icon="mdi-arrow-right"
|
||||
@click="startLogin"
|
||||
:loading="accountStore.fetchInProgress"
|
||||
color="green"
|
||||
>
|
||||
{{ $t('account.login.login') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</card-view>
|
||||
</template>
|
||||
88
src/pages/account/loginPage/registerForm.vue
Normal file
88
src/pages/account/loginPage/registerForm.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import { getEmailRules, getPasswordRules, getStringRules } from '@/scripts/validationRules';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false })
|
||||
const accountStore = useAccountStore()
|
||||
const router = useRouter()
|
||||
|
||||
async function registerAccount() {
|
||||
accountStore.registerAccount()
|
||||
.then(result => {
|
||||
if (result) {
|
||||
showRegisterCard.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('account.register')"
|
||||
icon="mdi-account-plus"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.username')"
|
||||
prepend-icon="mdi-account"
|
||||
v-model="accountStore.registerData.username"
|
||||
clearable
|
||||
hide-details
|
||||
variant="outlined"
|
||||
:rules="getStringRules()"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.password')"
|
||||
prepend-icon="mdi-key"
|
||||
type="password"
|
||||
v-model="accountStore.registerData.password"
|
||||
clearable
|
||||
hide-details
|
||||
variant="outlined"
|
||||
:rules="getPasswordRules()"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('account.userData.email')"
|
||||
prepend-icon="mdi-mail"
|
||||
v-model="accountStore.registerData.email"
|
||||
:rules="getEmailRules()"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
prepend-icon="mdi-arrow-left"
|
||||
@click="showRegisterCard = false"
|
||||
:disabled="accountStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('account.login.backToLogin') }}
|
||||
</outlined-button>
|
||||
|
||||
<outlined-button
|
||||
prepend-icon="mdi-account-plus"
|
||||
@click="registerAccount"
|
||||
:loading="accountStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('account.register') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</card-view>
|
||||
</template>
|
||||
48
src/pages/account/ordersPage/index.vue
Normal file
48
src/pages/account/ordersPage/index.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import orderItem from './orderItem.vue';
|
||||
import accountSubPageLayout from '@/layouts/accountSubPageLayout.vue';
|
||||
import circularProgressIndeterminate from '@/components/basics/circularProgressIndeterminate.vue';
|
||||
import { useOrderStore } from '@/stores/order.store';
|
||||
|
||||
const accountStore = useAccountStore()
|
||||
const orderStore = useOrderStore()
|
||||
|
||||
orderStore.getOrdersOfAccount(accountStore.userAccount)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<account-sub-page-layout>
|
||||
<!-- During fetching state -->
|
||||
<v-row
|
||||
v-if="orderStore.fetchInProgress"
|
||||
>
|
||||
<v-col class="text-center">
|
||||
<circular-progress-indeterminate />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Display all orders -->
|
||||
<v-row
|
||||
v-else-if="orderStore.orders.length > 0"
|
||||
v-for="order in orderStore.orders"
|
||||
>
|
||||
<v-col>
|
||||
<order-item
|
||||
:order="order"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- No orders -->
|
||||
<v-row v-else>
|
||||
<v-col>
|
||||
<v-empty-state
|
||||
icon="mdi-basket-off"
|
||||
:title="$t('order.noOrders')"
|
||||
:text="$t('order.noOrdersText')"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</account-sub-page-layout>
|
||||
</template>
|
||||
58
src/pages/account/ordersPage/orderItem.vue
Normal file
58
src/pages/account/ordersPage/orderItem.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import ticketListItem from '@/components/pageParts/ticketListItem.vue';
|
||||
import { OrderApiModel } from '@/data/models/apiEndpoints/orderApiModel';
|
||||
import moment from 'moment';
|
||||
|
||||
defineProps({
|
||||
order: OrderApiModel
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('order.orderedAt') + ' ' + moment(order.orderedAt).format('DD.MM.YY, HH:mm') + ' ' + $t('order.oclock')"
|
||||
variant="outlined"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-card variant="outlined" class="ml-5 pa-3">
|
||||
<div class="text-h6">
|
||||
<v-icon icon="mdi-home" />
|
||||
{{ $t('account.userData.address', 1) }}
|
||||
</div>
|
||||
|
||||
<div class="pl-9">{{ order.address.street }} {{ order.address.houseNumber }}</div>
|
||||
<div class="pl-9">{{ order.address.postalCode }} {{ order.address.city }}</div>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col>
|
||||
<v-card variant="outlined" class="mr-5 pa-3">
|
||||
<div class="text-h6">
|
||||
<v-icon icon="mdi-currency-usd" />
|
||||
{{ $t('account.userData.payment', 1) }}
|
||||
</div>
|
||||
<div class="pl-9">{{ order.payment.bankName }}</div>
|
||||
<div class="pl-9">{{ order.payment.iban }}</div>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-for="ticket of order.tickets">
|
||||
<v-col>
|
||||
<ticket-list-item
|
||||
:concert="ticket.concert"
|
||||
:event="ticket.concert.name"
|
||||
:band="ticket.concert.band"
|
||||
:location="ticket.concert.location"
|
||||
:city="ticket.concert.location.city"
|
||||
:image="ticket.concert.image"
|
||||
:seat-nr="ticket.seat.seatNr"
|
||||
:seat-group="ticket.seat.seatRow.seatGroup.name"
|
||||
:seat-row="ticket.seat.seatRow.row"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</card-view>
|
||||
</template>
|
||||
48
src/pages/admin/accountsAdminPage/index.vue
Normal file
48
src/pages/admin/accountsAdminPage/index.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import adminDataLayout from '@/layouts/adminDataLayout.vue';
|
||||
import { useFeedbackStore } from '@/stores/feedback.store';
|
||||
|
||||
const accountStore = useAccountStore()
|
||||
const feedbackStore = useFeedbackStore()
|
||||
|
||||
const headers = [
|
||||
{ title: feedbackStore.i18n.t('account.userData.username'), value: "username" },
|
||||
{ title: feedbackStore.i18n.t('account.userData.email'), value: "email" },
|
||||
{ title: feedbackStore.i18n.t('account.userData.firstName'), value: "firstName" },
|
||||
{ title: feedbackStore.i18n.t('account.userData.lastName'), value: "lastName" },
|
||||
{ title: feedbackStore.i18n.t('account.accountRole'), value: "accountRole.name" },
|
||||
{ title: "", value: "edit", width: 130 }
|
||||
]
|
||||
|
||||
accountStore.getAllAccounts()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<admin-data-layout
|
||||
:add-button-string="$t('account.addNewAccount')"
|
||||
:fetch-in-progress="accountStore.fetchInProgress"
|
||||
>
|
||||
<v-data-table
|
||||
:items="accountStore.accounts"
|
||||
:loading="accountStore.fetchInProgress"
|
||||
:headers="headers"
|
||||
>
|
||||
<template #item.edit="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
variant="plain"
|
||||
color="orange"
|
||||
@click="accountStore.editAccount(item)"
|
||||
/>
|
||||
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
variant="plain"
|
||||
color="red"
|
||||
@click="accountStore.deleteAccount(item)"
|
||||
/>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</admin-data-layout>
|
||||
</template>
|
||||
104
src/pages/admin/bandsAdminPage/bandEditDialog.vue
Normal file
104
src/pages/admin/bandsAdminPage/bandEditDialog.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<script setup lang="ts">
|
||||
import actionDialog from '@/components/basics/actionDialog.vue';
|
||||
import OutlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { GenreModel } from '@/data/models/acts/genreModel';
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import { useGenreStore } from '@/stores/genre.store';
|
||||
|
||||
const bandStore = useBandStore()
|
||||
const genreStore = useGenreStore()
|
||||
|
||||
function itemProps(item: GenreModel) {
|
||||
return {
|
||||
title: item.name
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<action-dialog
|
||||
v-model="bandStore.showEditDialog"
|
||||
:title="$t('band.editBand')"
|
||||
icon="mdi-pencil"
|
||||
>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('band.name')"
|
||||
v-model="bandStore.band.name"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('band.foundingYear')"
|
||||
v-model="bandStore.band.foundingYear"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col>
|
||||
<v-select
|
||||
:label="$t('band.genre', 2)"
|
||||
v-model="bandStore.band.genres"
|
||||
:items="genreStore.genres"
|
||||
:item-props="itemProps"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
chips
|
||||
multiple
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-textarea
|
||||
:label="$t('band.descriptionDe')"
|
||||
v-model="bandStore.band.descriptionDe"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col>
|
||||
<v-textarea
|
||||
:label="$t('band.descriptionEn')"
|
||||
v-model="bandStore.band.descriptionEn"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-img
|
||||
:src="bandStore.band.logo"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-img
|
||||
max-width="300"
|
||||
:src="bandStore.band.imageMembers"
|
||||
placeholder=""
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
color="green"
|
||||
@click="bandStore.saveBand"
|
||||
:loading="bandStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('misc.actions.save') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</action-dialog>
|
||||
</template>
|
||||
78
src/pages/admin/bandsAdminPage/index.vue
Normal file
78
src/pages/admin/bandsAdminPage/index.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<script setup lang="ts">
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import bandEditDialog from './bandEditDialog.vue';
|
||||
import adminDataLayout from '@/layouts/adminDataLayout.vue';
|
||||
import { useFeedbackStore } from '@/stores/feedback.store';
|
||||
|
||||
const bandStore = useBandStore()
|
||||
const feedbackStore = useFeedbackStore()
|
||||
|
||||
const headers = [
|
||||
{ title: feedbackStore.i18n.t('band.name'), value: "name" },
|
||||
{ title: feedbackStore.i18n.t('band.foundingYear'), value: "foundingYear" },
|
||||
{ title: feedbackStore.i18n.t('band.genre', 2), value: "genres" },
|
||||
{ title: feedbackStore.i18n.t('band.logo', 2), value: "logo" },
|
||||
{ title: feedbackStore.i18n.t('band.imageMember', 2), value: "imageMembers" },
|
||||
{ title: feedbackStore.i18n.t('band.image', 2), value: "images" },
|
||||
{ title: feedbackStore.i18n.t('concert.concert', 2), value: "nrOfConcerts" },
|
||||
{ title: "", value: "edit", width: 130 }
|
||||
]
|
||||
|
||||
bandStore.getBands()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<admin-data-layout
|
||||
:add-button-string="$t('band.addNewBand')"
|
||||
:fetch-in-progress="bandStore.fetchInProgress"
|
||||
:on-add-click="() => bandStore.newBand()"
|
||||
>
|
||||
<v-data-table
|
||||
:items="bandStore.bands"
|
||||
:headers="headers"
|
||||
:loading="bandStore.fetchInProgress"
|
||||
>
|
||||
<template #item.genres="{ item }">
|
||||
<v-chip v-for="genre of item.genres" class="mx-1">
|
||||
{{ genre.name }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<template #item.logo="{ item }">
|
||||
<v-icon
|
||||
:icon="item.logo != '' ? 'mdi-check' : 'mdi-close'"
|
||||
:color="item.logo != '' ? 'green' : 'red'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item.imageMembers="{ item }">
|
||||
<v-icon
|
||||
:icon="item.imageMembers != '' ? 'mdi-check' : 'mdi-close'"
|
||||
:color="item.imageMembers != '' ? 'green' : 'red'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item.images="{ item }">
|
||||
{{ item.images.length }} {{ $t('band.image', item.images.length) }}
|
||||
</template>
|
||||
|
||||
<template #item.edit="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
variant="plain"
|
||||
color="orange"
|
||||
@click="bandStore.editBand(item.name)"
|
||||
/>
|
||||
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
variant="plain"
|
||||
color="red"
|
||||
@click="bandStore.deleteBand(item.id)"
|
||||
/>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</admin-data-layout>
|
||||
|
||||
<band-edit-dialog />
|
||||
</template>
|
||||
77
src/pages/admin/concertsAdminPage/index.vue
Normal file
77
src/pages/admin/concertsAdminPage/index.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import { useConcertStore } from '@/stores/concert.store';
|
||||
import { useFeedbackStore } from '@/stores/feedback.store';
|
||||
import adminDataLayout from '@/layouts/adminDataLayout.vue';
|
||||
import moment from 'moment';
|
||||
|
||||
const concertStore = useConcertStore()
|
||||
const bandStore = useBandStore()
|
||||
const feedbackStore = useFeedbackStore()
|
||||
|
||||
const headers = [
|
||||
{ title: feedbackStore.i18n.t('concert.date'), value: "date" },
|
||||
{ title: feedbackStore.i18n.t('concert.name'), value: "name" },
|
||||
{ title: feedbackStore.i18n.t('band.name'), value: "band.name" },
|
||||
{ title: feedbackStore.i18n.t('location.name'), value: "location.name" },
|
||||
{ title: feedbackStore.i18n.t('concert.inStock'), value: "inStock" },
|
||||
{ title: feedbackStore.i18n.t('concert.offered'), value: "offered" },
|
||||
{ title: feedbackStore.i18n.t('concert.image'), value: "image" },
|
||||
{ title: feedbackStore.i18n.t('concert.price'), value: "price" },
|
||||
{ title: "", value: "edit", width: 130 }
|
||||
]
|
||||
|
||||
concertStore.getConcerts()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<admin-data-layout
|
||||
:add-button-string="$t('concert.addNewConcert')"
|
||||
:fetch-in-progress="concertStore.fetchInProgress"
|
||||
:on-add-click="() => concertStore.newConcert()"
|
||||
>
|
||||
<v-data-table
|
||||
:items="concertStore.concerts"
|
||||
:loading="concertStore.fetchInProgress"
|
||||
:headers="headers"
|
||||
>
|
||||
<template #item.date="{ item }">
|
||||
{{ moment(item.date).format("dd, DD.MM.YYYY") }}
|
||||
</template>
|
||||
|
||||
<template #item.price="{ item }">
|
||||
{{ item.price.toFixed(2) }} €
|
||||
</template>
|
||||
|
||||
<template #item.image="{ item }">
|
||||
<v-icon
|
||||
:icon="item.image != '' ? 'mdi-check' : 'mdi-close'"
|
||||
:color="item.image != '' ? 'green' : 'red'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item.offered="{ item }">
|
||||
<v-icon
|
||||
:icon="item.offered ? 'mdi-check' : 'mdi-close'"
|
||||
:color="item.offered ? 'green' : 'red'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item.edit="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
variant="plain"
|
||||
color="orange"
|
||||
@click="concertStore.editConcert(item)"
|
||||
/>
|
||||
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
variant="plain"
|
||||
color="red"
|
||||
@click="concertStore.deleteConcert(item)"
|
||||
/>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</admin-data-layout>
|
||||
</template>
|
||||
50
src/pages/admin/dashboardPage/dashboardCard.vue
Normal file
50
src/pages/admin/dashboardPage/dashboardCard.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
defineProps({
|
||||
title: String,
|
||||
icon: String,
|
||||
firstLine: String,
|
||||
secondLine: String,
|
||||
buttonRoute: String,
|
||||
loading: Boolean
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-col cols="12" md="6" lg="4">
|
||||
<card-view
|
||||
:title="title"
|
||||
:icon="icon"
|
||||
>
|
||||
<v-skeleton-loader
|
||||
type="heading"
|
||||
:loading="loading"
|
||||
class="text-h4 d-flex justify-center"
|
||||
>
|
||||
{{ firstLine }}
|
||||
</v-skeleton-loader>
|
||||
|
||||
<v-skeleton-loader
|
||||
type="text"
|
||||
:loading="loading"
|
||||
class="text-h6 text-disabled d-flex justify-center"
|
||||
>
|
||||
{{ secondLine }}
|
||||
</v-skeleton-loader>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
@click="router.push(buttonRoute)"
|
||||
:loading="loading"
|
||||
>
|
||||
{{ $t('misc.actions.more') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</card-view>
|
||||
</v-col>
|
||||
</template>
|
||||
119
src/pages/admin/dashboardPage/index.vue
Normal file
119
src/pages/admin/dashboardPage/index.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import { useConcertStore } from '@/stores/concert.store';
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
import { useGenreStore } from '@/stores/genre.store';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
import dashboardCard from './dashboardCard.vue';
|
||||
import { useOrderStore } from '@/stores/order.store';
|
||||
import { useFilesStore } from '@/stores/files.store';
|
||||
|
||||
const concertStore = useConcertStore()
|
||||
const bandStore = useBandStore()
|
||||
const accountStore = useAccountStore()
|
||||
const genreStore = useGenreStore()
|
||||
const locationStore = useLocationStore()
|
||||
const preferencesStore = usePreferencesStore()
|
||||
const orderStore = useOrderStore()
|
||||
const filesStore = useFilesStore()
|
||||
|
||||
filesStore.getStaticFolders()
|
||||
bandStore.getBands()
|
||||
locationStore.getLocations()
|
||||
genreStore.getGenres()
|
||||
accountStore.getAllAccounts()
|
||||
concertStore.getConcerts()
|
||||
orderStore.getAllOrders()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<dashboard-card
|
||||
:title="$t('band.band', 2)"
|
||||
icon="mdi-guitar-electric"
|
||||
:first-line="bandStore.bands.length + ' ' + $t('band.band', 2)"
|
||||
:second-line="bandStore.bands.reduce((counter, band) => {
|
||||
return band.concerts.length == 0 ? counter += 1 : counter
|
||||
}, 0) + ' ' + $t('band.withoutConcert', 2)"
|
||||
button-route="/admin/bands"
|
||||
:loading="bandStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
<dashboard-card
|
||||
:title="$t('concert.concert', 2)"
|
||||
icon="mdi-ticket"
|
||||
:first-line="concertStore.concerts.length + ' ' + $t('concert.concert', 2)"
|
||||
:second-line="concertStore.concerts.reduce((counter, obj) => {
|
||||
if (obj.inStock == 0) {
|
||||
counter += 1
|
||||
}
|
||||
|
||||
return counter
|
||||
}, 0) + ' ' + $t('concert.concertSoldOut')"
|
||||
button-route="/admin/concerts"
|
||||
:loading="concertStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
|
||||
<dashboard-card
|
||||
:title="$t('location.location', 2)"
|
||||
icon="mdi-city"
|
||||
:first-line="locationStore.locations.length + ' ' + $t('location.location', 2)"
|
||||
:second-line="locationStore.locations.reduce((city, obj) => {
|
||||
city[obj.city.name] =
|
||||
city[obj.city.name] === undefined ? city.push(obj.city.name) : city[obj.city.name] += 1
|
||||
|
||||
return city
|
||||
}, []).length + ' ' + $t('location.city', 2)"
|
||||
button-route="/admin/locations"
|
||||
:loading="locationStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
<dashboard-card
|
||||
:title="$t('account.account', 2)"
|
||||
icon="mdi-account"
|
||||
:first-line="accountStore.accounts.length + ' ' + $t('account.account', 2)"
|
||||
:second-line="accountStore.accounts.reduce((counter, obj) => {
|
||||
return obj.accountRole.privilegeAdminPanel ? counter += 1 : counter
|
||||
}, 0) + ' ' + $t('account.administrator', 2)"
|
||||
button-route="/admin/accounts"
|
||||
:loading="accountStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
<dashboard-card
|
||||
:title="$t('band.genre', 2)"
|
||||
icon="mdi-music-clef-treble"
|
||||
:first-line="genreStore.genres.length + ' ' + $t('band.genre', 2)"
|
||||
:second-line="genreStore.genres.reduce((counter, obj) => {
|
||||
return obj.bands.length == 0 ? counter += 1 : counter
|
||||
}, 0) + ' ' + $t('genre.withoutBand', 2)"
|
||||
button-route="/admin/genres"
|
||||
:loading="genreStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
<dashboard-card
|
||||
:title="$t('misc.file', 2)"
|
||||
icon="mdi-file"
|
||||
:first-line="filesStore.staticFolders.reduce((counter, obj) => {
|
||||
return counter + obj.nrOfItems
|
||||
}, 0) + ' ' + $t('misc.file', 2)"
|
||||
:second-line="filesStore.staticFolders.length + ' ' + $t('misc.folder', 2)"
|
||||
button-route="/admin/files"
|
||||
:loading="preferencesStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
<dashboard-card
|
||||
:title="$t('order.order', 2)"
|
||||
icon="mdi-basket"
|
||||
:first-line="orderStore.orders.length + ' ' + $t('order.order', 2)"
|
||||
:second-line="orderStore.orders.reduce((counter, obj) => {
|
||||
return !obj.shipped ? counter += 1 : counter
|
||||
}, 0) + ' ' + $t('order.notShipped')"
|
||||
button-route="/admin/orders"
|
||||
:loading="orderStore.fetchInProgress"
|
||||
/>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
64
src/pages/admin/filesAdminPage/fileUploadDialog.vue
Normal file
64
src/pages/admin/filesAdminPage/fileUploadDialog.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import actionDialog from '@/components/basics/actionDialog.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { useFilesStore } from '@/stores/files.store';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const filesStore = useFilesStore()
|
||||
|
||||
const test = ref()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<action-dialog
|
||||
v-model="filesStore.showFileUploadDialog"
|
||||
:title="$t('misc.uploadFile')"
|
||||
icon="mdi-file"
|
||||
max-width="800"
|
||||
>
|
||||
<v-form :model-value="test">
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-file-input
|
||||
v-model="filesStore.fileUpload"
|
||||
clearable
|
||||
:label="$t('misc.chooseFile')"
|
||||
:disabled="filesStore.fetchInProgress"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-radio-group
|
||||
v-model="filesStore.fileUploadDir"
|
||||
:label="$t('misc.chooseDestinationFolder')"
|
||||
:disabled="filesStore.fetchInProgress"
|
||||
>
|
||||
<v-radio
|
||||
v-for="folder of filesStore.staticFolders"
|
||||
:label="folder.name + '/'"
|
||||
:value="folder.name"
|
||||
/>
|
||||
</v-radio-group>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-btn type="submit">Submit</v-btn>
|
||||
</v-form>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
@click="filesStore.uploadFile"
|
||||
prepend-icon="mdi-file-upload"
|
||||
color="green"
|
||||
:disabled="filesStore.fileUploadDir.length == 0 || filesStore.fileUpload == undefined"
|
||||
:loading="filesStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('misc.upload') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</action-dialog>
|
||||
</template>
|
||||
64
src/pages/admin/filesAdminPage/index.vue
Normal file
64
src/pages/admin/filesAdminPage/index.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import adminDataLayout from '@/layouts/adminDataLayout.vue';
|
||||
import { ref } from 'vue';
|
||||
import FileUploadDialog from './fileUploadDialog.vue';
|
||||
import { useFilesStore } from '@/stores/files.store';
|
||||
|
||||
const filesStore = useFilesStore()
|
||||
const showPreviewDialog = ref(false)
|
||||
const previewFile = ref("")
|
||||
|
||||
filesStore.getStaticFolders()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<admin-data-layout
|
||||
:add-button-string="$t('misc.uploadFile')"
|
||||
:fetch-in-progress="filesStore.fetchInProgress"
|
||||
:on-add-click="() => { filesStore.showFileUploadDialog = true }"
|
||||
>
|
||||
<v-row >
|
||||
<v-col cols="2" class="border">
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="folder of filesStore.staticFolders"
|
||||
:key="folder.name"
|
||||
:value="folder"
|
||||
:title="folder.name + '/'"
|
||||
@click="filesStore.selectedFolder = folder; filesStore.getStaticFiles()"
|
||||
/>
|
||||
</v-list>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="4" class="border">
|
||||
<v-skeleton-loader
|
||||
:loading="filesStore.fetchInProgress"
|
||||
type="list-item-two-line"
|
||||
>
|
||||
<v-list max-height="800" class="w-100">
|
||||
<v-list-item
|
||||
v-for="file of filesStore.staticFiles"
|
||||
:title="file.name"
|
||||
:value="file.name"
|
||||
:subtitle="Math.round(file.size / 1024) + ' KB'"
|
||||
@click="() => { filesStore.selectedFile = file }"
|
||||
/>
|
||||
</v-list>
|
||||
</v-skeleton-loader>
|
||||
</v-col>
|
||||
|
||||
<v-col class="border">
|
||||
<v-img
|
||||
v-if="filesStore.selectedFile != undefined"
|
||||
:src="filesStore.selectedFile.url" max-height="400" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</admin-data-layout>
|
||||
|
||||
<file-preview-dialog
|
||||
v-model:show-dialog="showPreviewDialog"
|
||||
:url="previewFile"
|
||||
/>
|
||||
|
||||
<file-upload-dialog />
|
||||
</template>
|
||||
47
src/pages/admin/genresAdminPage/genreEditDialog.vue
Normal file
47
src/pages/admin/genresAdminPage/genreEditDialog.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import actionDialog from '@/components/basics/actionDialog.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { getStringRules } from '@/scripts/validationRules';
|
||||
import { useGenreStore } from '@/stores/genre.store';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const valid = ref(false)
|
||||
|
||||
const genreStore = useGenreStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<action-dialog
|
||||
v-model="genreStore.showEditDialog"
|
||||
:title="$t('band.editGenre')"
|
||||
icon="mdi-pencil"
|
||||
max-width="500"
|
||||
>
|
||||
<v-form v-model="valid">
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
:label="$t('band.genre')"
|
||||
v-model="genreStore.genre.name"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
:rules="getStringRules(3)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-form>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
color="green"
|
||||
@click="genreStore.saveGenre"
|
||||
:disabled="!valid"
|
||||
:loading="genreStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('misc.actions.save') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</action-dialog>
|
||||
</template>
|
||||
54
src/pages/admin/genresAdminPage/index.vue
Normal file
54
src/pages/admin/genresAdminPage/index.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<script setup lang="ts">
|
||||
import adminDataLayout from '@/layouts/adminDataLayout.vue';
|
||||
import genreEditDialog from './genreEditDialog.vue';
|
||||
import { useGenreStore } from '@/stores/genre.store';
|
||||
|
||||
const genreStore = useGenreStore()
|
||||
|
||||
const headers = [
|
||||
{ title: "Name", value: "name" },
|
||||
{ title: "Bands", value: "bands" },
|
||||
{ title: "", value: "edit", width: 130 }
|
||||
]
|
||||
|
||||
genreStore.getGenres()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<admin-data-layout
|
||||
:add-button-string="$t('band.addNewGenre')"
|
||||
:fetch-in-progress="genreStore.fetchInProgress"
|
||||
:on-add-click="() => { genreStore.newGenre() }"
|
||||
>
|
||||
<v-data-table
|
||||
:loading="genreStore.fetchInProgress"
|
||||
:items="genreStore.genres"
|
||||
:headers="headers"
|
||||
:items-per-page="100"
|
||||
>
|
||||
<template #item.bands="{ item }">
|
||||
<v-chip v-for="band of item.bands" class="mx-1">
|
||||
{{ band.name }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<template #item.edit="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
variant="plain"
|
||||
color="orange"
|
||||
@click="genreStore.editGenre(item)"
|
||||
/>
|
||||
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
variant="plain"
|
||||
color="red"
|
||||
@click="genreStore.deleteGenre(item)"
|
||||
/>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</admin-data-layout>
|
||||
|
||||
<genre-edit-dialog />
|
||||
</template>
|
||||
70
src/pages/admin/locationsAdminPage/index.vue
Normal file
70
src/pages/admin/locationsAdminPage/index.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import adminDataLayout from '@/layouts/adminDataLayout.vue';
|
||||
import { useFeedbackStore } from '@/stores/feedback.store';
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
|
||||
const locationStore = useLocationStore()
|
||||
const feedbackStore = useFeedbackStore()
|
||||
|
||||
const headers = [
|
||||
{ title: feedbackStore.i18n.t('location.name'), value: "name" },
|
||||
{ title: feedbackStore.i18n.t('location.address'), value: "address" },
|
||||
{ title: feedbackStore.i18n.t('location.imageIndoor'), value: "imageIndoor" },
|
||||
{ title: feedbackStore.i18n.t('location.imageOutdoor'), value: "imageOutdoor" },
|
||||
{ title: feedbackStore.i18n.t('location.layoutNr'), value: "layout" },
|
||||
{ title: feedbackStore.i18n.t('location.capacity'), value: "capacity" },
|
||||
{ title: feedbackStore.i18n.t('location.city'), value: "city" },
|
||||
{ title: feedbackStore.i18n.t('location.nrOfConcerts'), value: "nrOfConcerts" },
|
||||
{ title: "", value: "edit", width: 130 }
|
||||
]
|
||||
|
||||
locationStore.getLocations()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<admin-data-layout
|
||||
:fetch-in-progress="locationStore.fetchInProgress"
|
||||
:add-button-string="$t('location.addLocation')"
|
||||
:on-add-click="() => { locationStore.newLocation() }"
|
||||
>
|
||||
<v-data-table
|
||||
:items="locationStore.locations"
|
||||
:headers="headers"
|
||||
:loading="locationStore.fetchInProgress"
|
||||
>
|
||||
<template #item.imageIndoor="{ item }">
|
||||
<v-icon
|
||||
:icon="item.imageIndoor != '' ? 'mdi-check' : 'mdi-close'"
|
||||
:color="item.imageIndoor != '' ? 'green' : 'red'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item.imageOutdoor="{ item }">
|
||||
<v-icon
|
||||
:icon="item.imageOutdoor != '' ? 'mdi-check' : 'mdi-close'"
|
||||
:color="item.imageOutdoor != '' ? 'green' : 'red'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item.city="{ item }">
|
||||
{{ item.city.name }}
|
||||
</template>
|
||||
|
||||
<template #item.edit="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
variant="plain"
|
||||
color="orange"
|
||||
@click="locationStore.editLocation(item)"
|
||||
/>
|
||||
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
variant="plain"
|
||||
color="red"
|
||||
@click="locationStore.deleteLocation(item)"
|
||||
/>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</admin-data-layout>
|
||||
</template>
|
||||
72
src/pages/admin/ordersAdminPage/index.vue
Normal file
72
src/pages/admin/ordersAdminPage/index.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import adminDataLayout from '@/layouts/adminDataLayout.vue';
|
||||
import { useOrderStore } from '@/stores/order.store';
|
||||
import moment from 'moment';
|
||||
import OrderDetailDialog from './orderDetailDialog.vue';
|
||||
|
||||
const orderStore = useOrderStore()
|
||||
|
||||
const headers = [
|
||||
{ title: "Account", value: "account.username" },
|
||||
{ title: "Name", value: "account" },
|
||||
{ title: "Bestellt am", value: "orderedAt" },
|
||||
{ title: "Adresse", value: "street" },
|
||||
{ title: "Stadt", value: "city" },
|
||||
{ title: "Versendet", value: "shipped" },
|
||||
{ title: "", value: "edit", width: 130 }
|
||||
]
|
||||
|
||||
orderStore.getAllOrders()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<admin-data-layout
|
||||
:hide-add-button="true"
|
||||
>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="orderStore.orders"
|
||||
>
|
||||
<template #item.account="{ item }">
|
||||
{{ item.account.firstName }} {{ item.account.lastName }}
|
||||
</template>
|
||||
|
||||
<template #item.orderedAt="{ item }">
|
||||
{{ moment(item.orderedAt).format("DD.MM.YYYY, HH:mm:ss") }}
|
||||
</template>
|
||||
|
||||
<template #item.street="{ item }">
|
||||
{{ item.address.street }} {{ item.address.houseNumber }}
|
||||
</template>
|
||||
|
||||
<template #item.city="{ item }">
|
||||
{{ item.address.postalCode }} {{ item.address.city }}
|
||||
</template>
|
||||
|
||||
<template #item.shipped="{ item }">
|
||||
<v-icon
|
||||
:icon="item.shipped ? 'mdi-check' : 'mdi-close'"
|
||||
:color="item.shipped ? 'green' : 'red'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item.edit="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-eye"
|
||||
variant="plain"
|
||||
@click="orderStore.openDetails(item)"
|
||||
/>
|
||||
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
variant="plain"
|
||||
color="red"
|
||||
@click="orderStore.deleteOrder(item)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
</v-data-table>
|
||||
</admin-data-layout>
|
||||
|
||||
<order-detail-dialog />
|
||||
</template>
|
||||
27
src/pages/admin/ordersAdminPage/orderDetailDialog.vue
Normal file
27
src/pages/admin/ordersAdminPage/orderDetailDialog.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import actionDialog from '@/components/basics/actionDialog.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { useOrderStore } from '@/stores/order.store';
|
||||
import moment from 'moment';
|
||||
|
||||
const orderStore = useOrderStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<action-dialog
|
||||
v-model="orderStore.showDetailDialog"
|
||||
:title="$t('order.order')"
|
||||
icon="mdi-basket"
|
||||
>
|
||||
<v-list>
|
||||
<v-list-subheader>
|
||||
{{ $t('ticket.ticket', 2) }}
|
||||
</v-list-subheader>
|
||||
|
||||
<v-list-item v-for="ticket of orderStore.order.tickets">
|
||||
{{ moment(ticket.concert.date).format("DD.MM.YYYY") }} -
|
||||
{{ ticket.concert.band.name }} - {{ ticket.concert.name }}
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</action-dialog>
|
||||
</template>
|
||||
35
src/pages/bands/bandDetailPage/bandMemberSection.vue
Normal file
35
src/pages/bands/bandDetailPage/bandMemberSection.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import cardWithTopImage from '@/components/basics/cardViewTopImage.vue';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
|
||||
const bandStore = useBandStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('band.bandMember')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-if="bandStore.fetchInProgress" >
|
||||
<v-col cols="6" md="3" v-for="i in 4">
|
||||
<card-with-top-image :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col v-for="member of bandStore.band.members" cols="6" md="3">
|
||||
<card-with-top-image
|
||||
:title="member.name"
|
||||
:image=" member.image"
|
||||
:link="false"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
</v-row>
|
||||
</template>
|
||||
36
src/pages/bands/bandDetailPage/concertSection.vue
Normal file
36
src/pages/bands/bandDetailPage/concertSection.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import concertListItem from '@/components/pageParts/concertListItem.vue';
|
||||
import CardViewHorizontal from '@/components/basics/cardViewHorizontal.vue';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
|
||||
const bandStore = useBandStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('concert.concert', 2)" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-if="bandStore.fetchInProgress" v-for="i in 3">
|
||||
<v-col>
|
||||
<card-view-horizontal :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<div v-for="concert of bandStore.band.concerts">
|
||||
<v-row v-if="concert.offered">
|
||||
<v-col>
|
||||
<concert-list-item
|
||||
:concert="concert"
|
||||
:band="bandStore.band"
|
||||
:location="concert.location"
|
||||
:title="concert.location.city.name"
|
||||
:link="concert.inStock > 0"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
53
src/pages/bands/bandDetailPage/gallerySection.vue
Normal file
53
src/pages/bands/bandDetailPage/gallerySection.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
|
||||
const bandStore = useBandStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('band.image', 2)" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-skeleton-loader
|
||||
type="image"
|
||||
:loading="bandStore.fetchInProgress"
|
||||
>
|
||||
<v-carousel
|
||||
show-arrows
|
||||
hide-delimiter-background
|
||||
hide-delimiters
|
||||
max-height="900"
|
||||
>
|
||||
<template #prev="{ props }">
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="props.onClick"
|
||||
icon="mdi-chevron-left"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #next="{ props }">
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="props.onClick"
|
||||
icon="mdi-chevron-right"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
||||
<v-carousel-item
|
||||
v-for="image in bandStore.band.images"
|
||||
:src="image"
|
||||
cover
|
||||
/>
|
||||
</v-carousel>
|
||||
</v-skeleton-loader>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
53
src/pages/bands/bandDetailPage/index.vue
Normal file
53
src/pages/bands/bandDetailPage/index.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import ratingSection from './ratingSection.vue';
|
||||
import bandMemberSection from './bandMemberSection.vue';
|
||||
import gallerySection from './gallerySection.vue';
|
||||
import concertSection from './concertSection.vue';
|
||||
import heroImage from '@/components/pageParts/heroImage.vue';
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import { onMounted, watch } from 'vue';
|
||||
|
||||
const router = useRouter()
|
||||
const bandStore = useBandStore()
|
||||
|
||||
onMounted(async () => {
|
||||
bandStore.getBand(String(router.currentRoute.value.params.name).replaceAll('-', ' '))
|
||||
})
|
||||
|
||||
watch(() => router.currentRoute.value.params.name, () => {
|
||||
bandStore.getBand(String(router.currentRoute.value.params.name).replaceAll('-', ' '))
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<hero-image
|
||||
:image="bandStore.band.imageMembers"
|
||||
:logo="bandStore.band.logo"
|
||||
:title="bandStore.band.name"
|
||||
:chips="bandStore.band.genres.map(genre => genre.name)"
|
||||
:description="bandStore.band.descriptionDe"
|
||||
:loading="bandStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col cols="10">
|
||||
<concert-section />
|
||||
|
||||
<band-member-section />
|
||||
|
||||
<rating-section
|
||||
:rating="bandStore.band.rating"
|
||||
:ratings="bandStore.band.ratingValues"
|
||||
/>
|
||||
|
||||
<gallery-section />
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
70
src/pages/bands/bandDetailPage/ratingSection.vue
Normal file
70
src/pages/bands/bandDetailPage/ratingSection.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import { RatingModel } from '@/data/models/acts/ratingModel';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
|
||||
defineProps({
|
||||
/**
|
||||
* Overall rating of the band
|
||||
*/
|
||||
rating: Number,
|
||||
|
||||
/**
|
||||
* Array of rating steps from 1 to 5
|
||||
*/
|
||||
ratings: {
|
||||
type: Array<RatingModel>,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('band.rating', 2)" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div class="d-flex align-center justify-center flex-column" style="height: 100%;">
|
||||
<div class="text-h2 mt-5">
|
||||
{{ rating.toFixed(1) }}
|
||||
<span class="text-h6 ml-n3">/5</span>
|
||||
</div>
|
||||
|
||||
<v-rating
|
||||
:model-value="rating"
|
||||
color="yellow-darken-3"
|
||||
half-increments
|
||||
size="x-large"
|
||||
readonly
|
||||
/>
|
||||
|
||||
<div class="px-3 text-h6">{{ ratings.length }} {{ $t('band.rating', ratings.length) }}</div>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-col>
|
||||
<v-list>
|
||||
<v-list-item v-for="ratingValue in ratings">
|
||||
<template v-slot:prepend>
|
||||
<span>{{ ratingValue.value }}</span>
|
||||
<v-icon class="ml-3 mr-n3" icon="mdi-star" />
|
||||
</template>
|
||||
|
||||
<v-progress-linear
|
||||
:model-value="(ratingValue.count / ratings.length) * 100"
|
||||
height="20"
|
||||
color="yellow-darken-3"
|
||||
rounded
|
||||
/>
|
||||
|
||||
<template v-slot:append>
|
||||
<span class="d-flex justify-end" style="width: 25px;"> {{ ratingValue.count }} </span>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
50
src/pages/bands/bandsPage/bandFilterbar.vue
Normal file
50
src/pages/bands/bandsPage/bandFilterbar.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { GenreModel } from '@/data/models/acts/genreModel';
|
||||
import { useGenreStore } from '@/stores/genre.store';
|
||||
|
||||
const bandStore = useBandStore()
|
||||
const genreStore = useGenreStore()
|
||||
|
||||
genreStore.getGenres()
|
||||
|
||||
function itemProps(item: GenreModel) {
|
||||
return {
|
||||
title: item.name
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('misc.actions.filtering')"
|
||||
icon="mdi-filter"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-select
|
||||
v-model="genreStore.filteredGenres"
|
||||
:items="genreStore.genres"
|
||||
variant="outlined"
|
||||
:label="$t('band.genre', 2)"
|
||||
:item-props="itemProps"
|
||||
chips
|
||||
clearable
|
||||
hide-details
|
||||
multiple
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="auto">
|
||||
<outlined-button
|
||||
@click="bandStore.getBands"
|
||||
height="100%"
|
||||
>
|
||||
{{ $t('misc.actions.filtering') }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</card-view>
|
||||
</template>
|
||||
50
src/pages/bands/bandsPage/index.vue
Normal file
50
src/pages/bands/bandsPage/index.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import cardViewHorizontal from '@/components/basics/cardViewHorizontal.vue';
|
||||
import bandListItem from '@/components/pageParts/bandListItem.vue';
|
||||
import bandFilterbar from './bandFilterbar.vue';
|
||||
|
||||
const bandStore = useBandStore()
|
||||
|
||||
bandStore.getBands()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col cols="10">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<band-filterbar />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row
|
||||
v-if="bandStore.fetchInProgress"
|
||||
v-for="i in 3"
|
||||
>
|
||||
<v-col>
|
||||
<card-view-horizontal :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row
|
||||
v-else-if="bandStore.bands.length > 0"
|
||||
v-for="band in bandStore.bands"
|
||||
>
|
||||
<v-col>
|
||||
<band-list-item
|
||||
:band="band"
|
||||
:concerts="band.concerts"
|
||||
:genres="band.genres"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
130
src/pages/concerts/concertBookingPage/index.vue
Normal file
130
src/pages/concerts/concertBookingPage/index.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import seatPlanMap from '@/components/seatPlanMap/seatPlanMap.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import { useBasketStore } from '@/stores/basket.store';
|
||||
import concertListItem from '@/components/pageParts/concertListItem.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { useConcertStore } from '@/stores/concert.store';
|
||||
import ticketListItem from '@/components/pageParts/ticketListItem.vue';
|
||||
import circularProgressIndeterminate from '@/components/basics/circularProgressIndeterminate.vue';
|
||||
import { onMounted, watch } from 'vue';
|
||||
|
||||
const router = useRouter()
|
||||
const basketStore = useBasketStore()
|
||||
const concertStore = useConcertStore()
|
||||
|
||||
onMounted(async () => {
|
||||
concertStore.getConcert(
|
||||
String(router.currentRoute.value.params.locationUrl),
|
||||
String(router.currentRoute.value.params.date)
|
||||
)
|
||||
})
|
||||
|
||||
watch(() => router.currentRoute.value.params.locationUrl, () => {
|
||||
concertStore.getConcert(
|
||||
String(router.currentRoute.value.params.locationUrl),
|
||||
String(router.currentRoute.value.params.date)
|
||||
)
|
||||
})
|
||||
|
||||
watch(() => router.currentRoute.value.params.date, () => {
|
||||
concertStore.getConcert(
|
||||
String(router.currentRoute.value.params.locationUrl),
|
||||
String(router.currentRoute.value.params.date)
|
||||
)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col cols="10">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('concert.selectedConcert')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<concert-list-item
|
||||
:concert="concertStore.concert"
|
||||
:band="concertStore.concert.band"
|
||||
:location="concertStore.concert.location"
|
||||
:loading="concertStore.fetchInProgress"
|
||||
:link="false"
|
||||
:title="concertStore.concert.location.city.name"
|
||||
:show-button="false"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('location.seat.seatSelection')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row >
|
||||
<v-col class="text-center" v-if="concertStore.fetchInProgress">
|
||||
<circular-progress-indeterminate />
|
||||
|
||||
<div class="pt-5 text-h3">
|
||||
{{ $t('misc.loading') }}...
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-col v-else>
|
||||
<seat-plan-map
|
||||
:concert="concertStore.concert"
|
||||
:seat-groups="concertStore.concert.location.seatGroups"
|
||||
:location="concertStore.concert.location"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('order.orderSummary')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-list >
|
||||
<v-list-item v-for="seat in basketStore.selectedSeats" >
|
||||
<ticket-list-item
|
||||
:concert="concertStore.concert"
|
||||
:band="concertStore.concert.band"
|
||||
:location="concertStore.concert.location"
|
||||
:city="concertStore.concert.location.city"
|
||||
:image="concertStore.concert.image"
|
||||
:seat-group="seat.seatGroupName"
|
||||
:seat-nr="seat.seat.seatNr"
|
||||
:seat-row="seat.seatRow"
|
||||
/>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row class="pb-5">
|
||||
<outlined-button todo
|
||||
prepend-icon="mdi-basket-plus"
|
||||
@click="basketStore.moveSeatSelectionsToBasket(concertStore.concert.band);
|
||||
router.push('/basket')"
|
||||
:disabled="basketStore.selectedSeats.length == 0"
|
||||
block
|
||||
>
|
||||
{{ $t('basket.addToBasket') }}
|
||||
</outlined-button>
|
||||
</v-row>
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
51
src/pages/concerts/concertsPage/concertFilterbar.vue
Normal file
51
src/pages/concerts/concertsPage/concertFilterbar.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { CityModel } from '@/data/models/locations/cityModel';
|
||||
import { LocationModel } from '@/data/models/locations/locationModel';
|
||||
import { useConcertStore } from '@/stores/concert.store';
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
|
||||
const concertStore = useConcertStore()
|
||||
const locationStore = useLocationStore()
|
||||
|
||||
locationStore.getLocations()
|
||||
|
||||
function itemProps(item: CityModel) {
|
||||
return {
|
||||
title: item.name
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('misc.actions.filtering')"
|
||||
icon="mdi-filter"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-select
|
||||
v-model="concertStore.filteredCities"
|
||||
:items="locationStore.cities"
|
||||
variant="outlined"
|
||||
:label="$t('location.city', 2)"
|
||||
:item-props="itemProps"
|
||||
chips
|
||||
clearable
|
||||
hide-details
|
||||
multiple
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="auto">
|
||||
<outlined-button
|
||||
@click="concertStore.getConcerts"
|
||||
height="100%"
|
||||
>
|
||||
{{ $t('misc.actions.filtering') }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</card-view>
|
||||
</template>
|
||||
51
src/pages/concerts/concertsPage/concertsListSection.vue
Normal file
51
src/pages/concerts/concertsPage/concertsListSection.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { useConcertStore } from '@/stores/concert.store';
|
||||
import concertListItem from '@/components/pageParts/concertListItem.vue';
|
||||
import cardViewHorizontal from '@/components/basics/cardViewHorizontal.vue';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import concertFilterbar from './concertFilterbar.vue';
|
||||
|
||||
const concertStore = useConcertStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="concertStore.fetchInProgress"
|
||||
>
|
||||
<section-divider :loading="true" />
|
||||
<v-row v-for="i in 3">
|
||||
<v-col>
|
||||
<card-view-horizontal :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="concertStore.concerts.length > 0"
|
||||
v-for="(concert, index) of concertStore.concerts"
|
||||
>
|
||||
<div v-if="concert.offered">
|
||||
<v-row
|
||||
v-if="index == 0 ||
|
||||
new Date(concertStore.concerts[index - 1].date).getMonth() !=
|
||||
new Date(concertStore.concerts[index].date).getMonth()"
|
||||
>
|
||||
<v-col>
|
||||
<section-divider
|
||||
:title="new Date(concert.date).toLocaleString('default', { month: 'long' }) + ' ' + new Date(concert.date).getFullYear()"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<concert-list-item
|
||||
:concert="concert"
|
||||
:band="concert.band"
|
||||
:location="concert.location"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
32
src/pages/concerts/concertsPage/index.vue
Normal file
32
src/pages/concerts/concertsPage/index.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { useConcertStore } from '@/stores/concert.store';
|
||||
import concertFilterbar from './concertFilterbar.vue';
|
||||
import ConcertsListSection from './concertsListSection.vue';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
const concertStore = useConcertStore()
|
||||
|
||||
onMounted(async () => {
|
||||
concertStore.getConcerts()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col cols="10">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<concert-filterbar />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<concerts-list-section />
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
48
src/pages/locations/locationDetailPage/index.vue
Normal file
48
src/pages/locations/locationDetailPage/index.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import heroImage from '@/components/pageParts/heroImage.vue';
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
import locationConcertsSection from './locationConcertsSection.vue';
|
||||
import LocationSeatMapSection from './locationSeatMapSection.vue';
|
||||
import { onMounted, watch } from 'vue';
|
||||
|
||||
const router = useRouter()
|
||||
const locationStore = useLocationStore()
|
||||
|
||||
onMounted(async () => {
|
||||
locationStore.getLocationByName(String(router.currentRoute.value.params.name))
|
||||
})
|
||||
|
||||
watch(() => router.currentRoute.value.params.name, () => {
|
||||
locationStore.getLocationByName(String(router.currentRoute.value.params.name))
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<hero-image
|
||||
:title="locationStore.location.name"
|
||||
:image="locationStore.location.imageIndoor"
|
||||
:description="locationStore.location.address + locationStore.location.city.name"
|
||||
:loading="locationStore.fetchInProgress"
|
||||
:logo="locationStore.location.imageOutdoor"
|
||||
>
|
||||
<template #description>
|
||||
<p class="text-h6">{{ locationStore.location.address }}</p>
|
||||
<p class="text-h6">{{ locationStore.location.city.name }}</p>
|
||||
</template>
|
||||
</hero-image>
|
||||
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-spacer/>
|
||||
|
||||
<v-col cols="10">
|
||||
<location-concerts-section />
|
||||
|
||||
<location-seat-map-section />
|
||||
</v-col>
|
||||
|
||||
<v-spacer/>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
@@ -0,0 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
import cardViewHorizontal from '@/components/basics/cardViewHorizontal.vue';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import concertListItem from '@/components/pageParts/concertListItem.vue';
|
||||
|
||||
const locationStore = useLocationStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Concerts divider -->
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('concert.concert', 2)" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Display skeleton cards on fetching -->
|
||||
<v-row v-for="i in 3" v-if="locationStore.fetchInProgress">
|
||||
<v-col class="text-center">
|
||||
<card-view-horizontal :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Show concerts after fetching -->
|
||||
<div
|
||||
v-else-if="locationStore.location.concerts.length > 0"
|
||||
v-for="concert of locationStore.location.concerts"
|
||||
>
|
||||
<v-row
|
||||
v-if="concert.offered"
|
||||
>
|
||||
<v-col>
|
||||
<concert-list-item
|
||||
:concert="concert"
|
||||
:band="concert.band"
|
||||
:location="locationStore.location"
|
||||
:title="concert.name"
|
||||
>
|
||||
<template #description>
|
||||
{{ concert.band.name }}
|
||||
</template>
|
||||
</concert-list-item>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<!-- Show empty state if no items there -->
|
||||
<v-row v-else>
|
||||
<v-col>
|
||||
<v-empty-state
|
||||
icon="mdi-magnify"
|
||||
:title="$t('concert.noConcertsFound')"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
import seatPlanMap from '@/components/seatPlanMap/seatPlanMap.vue';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
|
||||
const locationStore = useLocationStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Seat Map divider -->
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('location.seat.seatPlan')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Loading circle during fetching -->
|
||||
<div v-if="locationStore.fetchInProgress">
|
||||
<v-col class="text-center">
|
||||
<circular-progress-indeterminate />
|
||||
</v-col>
|
||||
</div>
|
||||
|
||||
<!-- Seat map -->
|
||||
<v-row v-else>
|
||||
<v-col>
|
||||
<seat-plan-map
|
||||
:location="locationStore.location"
|
||||
:seat-groups="locationStore.location.seatGroups"
|
||||
:disabled="true"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
69
src/pages/locations/locationsPage/index.vue
Normal file
69
src/pages/locations/locationsPage/index.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import cardWithTopImage from '@/components/basics/cardViewTopImage.vue';
|
||||
import locationListItem from '@/components/pageParts/locationListItem.vue';
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
|
||||
const locationStore = useLocationStore()
|
||||
|
||||
locationStore.getLocations()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col cols="10">
|
||||
<!-- During fetching -->
|
||||
<div v-if="locationStore.fetchInProgress" v-for="i in 2">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col class="text-center" v-for="i in 4" cols="6" md="3">
|
||||
<card-with-top-image :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<!-- When all data are downloaded -->
|
||||
<div
|
||||
v-else-if="locationStore.locations.length > 0"
|
||||
v-for="city in locationStore.cities"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider
|
||||
:title="city.name"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="location in locationStore.getLocationsByCity(city.name)"
|
||||
cols="6"
|
||||
md="3"
|
||||
>
|
||||
<location-list-item
|
||||
:location="location"
|
||||
:nrOfConcerts="location.nrOfConcerts"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
68
src/pages/misc/basketPage/index.vue
Normal file
68
src/pages/misc/basketPage/index.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
import { useBasketStore } from '@/stores/basket.store';
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import orderingDialog from './orderingDialog.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { ref } from 'vue';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import ticketsTable from './ticketsTable.vue';
|
||||
|
||||
const basketStore = useBasketStore()
|
||||
const accountStore = useAccountStore()
|
||||
const showOrderingDialog = ref()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container max-width="1000">
|
||||
<v-row v-if="accountStore.userAccount.id == null">
|
||||
<v-col>
|
||||
<v-alert
|
||||
color="info"
|
||||
closable
|
||||
>
|
||||
{{ $t('account.login.pleaseLoginToOrder') }}
|
||||
</v-alert>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<card-view
|
||||
:title="$t('basket.basket')"
|
||||
v-model="showOrderingDialog"
|
||||
icon="mdi-cart"
|
||||
>
|
||||
<template #borderless>
|
||||
<!-- Display items if basket is not empty -->
|
||||
<tickets-table v-if="basketStore.itemsInBasket.length > 0"/>
|
||||
|
||||
<!-- Display empty state if card is empty -->
|
||||
<v-empty-state v-else
|
||||
icon="mdi-basket-off"
|
||||
:title="$t('basket.emptyBasketTitle')"
|
||||
:text="$t('basket.emptyBasketText')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-card-text class="text-right text-h5" v-if="basketStore.itemsInBasket.length > 0">
|
||||
{{ $t('misc.totalPrice') }}: {{ (basketStore.getTotalPrice).toFixed(2) }} €
|
||||
</v-card-text>
|
||||
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
prepend-icon="mdi-basket-check"
|
||||
:disabled="basketStore.itemsInBasket.length == 0 || accountStore.userAccount.id == null"
|
||||
variant="outlined"
|
||||
color="green"
|
||||
@click="showOrderingDialog = true"
|
||||
>
|
||||
{{ $t('order.takeOrder') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</card-view>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<ordering-dialog v-model="showOrderingDialog" />
|
||||
</template>
|
||||
109
src/pages/misc/basketPage/orderingDialog.vue
Normal file
109
src/pages/misc/basketPage/orderingDialog.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<script setup lang="ts">
|
||||
import actionDialog from '@/components/basics/actionDialog.vue';
|
||||
import { useBasketStore } from '@/stores/basket.store';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { ModelRef, ref } from 'vue';
|
||||
import { useAccountStore } from '@/stores/account.store';
|
||||
import { AddressModel } from '@/data/models/user/addressModel';
|
||||
import { PaymentModel } from '@/data/models/user/paymentModel';
|
||||
|
||||
const basketStore = useBasketStore()
|
||||
const accountStore = useAccountStore()
|
||||
const showDialog: ModelRef<boolean> = defineModel()
|
||||
const orderingInProgress = ref(false)
|
||||
const addressError = ref(false)
|
||||
const paymentError = ref(false)
|
||||
|
||||
async function doOrder() {
|
||||
orderingInProgress.value = true
|
||||
addressError.value = false
|
||||
paymentError.value = false
|
||||
|
||||
if (basketStore.usedAddress == null) {
|
||||
addressError.value = true
|
||||
}
|
||||
|
||||
if (basketStore.usedPayment == null){
|
||||
paymentError.value = true
|
||||
}
|
||||
|
||||
if (basketStore.usedAddress != null && basketStore.usedPayment != null) {
|
||||
await basketStore.takeOrder()
|
||||
showDialog.value = false
|
||||
}
|
||||
|
||||
orderingInProgress.value = false
|
||||
}
|
||||
|
||||
function addressItemProps(item: AddressModel) {
|
||||
return {
|
||||
title: item.street + " " + item.houseNumber,
|
||||
subtitle: item.postalCode + " " + item.city
|
||||
}
|
||||
}
|
||||
|
||||
function paymentItemProps(item: PaymentModel) {
|
||||
return {
|
||||
title: item.bankName,
|
||||
subtitle: item.iban
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<action-dialog
|
||||
:title="$t('order.ordering')"
|
||||
icon="mdi-basket-check"
|
||||
v-model="showDialog"
|
||||
max-width="800"
|
||||
persistent
|
||||
>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-select
|
||||
v-model="basketStore.usedAddress"
|
||||
:items="accountStore.userAccount.addresses"
|
||||
:item-props="addressItemProps"
|
||||
:label="$t('account.userData.address')"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-select
|
||||
v-model="basketStore.usedPayment"
|
||||
:items="accountStore.userAccount.payments"
|
||||
:item-props="paymentItemProps"
|
||||
:label="$t('account.userData.payment')"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button
|
||||
@click="showDialog = false"
|
||||
prepend-icon="mdi-close"
|
||||
color="orange"
|
||||
:disabled="orderingInProgress"
|
||||
>
|
||||
{{ $t('misc.actions.cancel') }}
|
||||
</outlined-button>
|
||||
|
||||
<outlined-button
|
||||
@click="doOrder"
|
||||
:loading="orderingInProgress"
|
||||
prepend-icon="mdi-send"
|
||||
color="green"
|
||||
>
|
||||
{{ $t('order.takeOrder') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</action-dialog>
|
||||
</template>
|
||||
67
src/pages/misc/basketPage/ticketsTable.vue
Normal file
67
src/pages/misc/basketPage/ticketsTable.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import { useBasketStore } from '@/stores/basket.store';
|
||||
import { BasketItemModel } from '@/data/models/ordering/basketItemModel';
|
||||
import { calcPrice } from '@/scripts/concertScripts';
|
||||
|
||||
const basketStore = useBasketStore()
|
||||
|
||||
function removeFromBasket(basketItem: BasketItemModel) {
|
||||
basketStore.removeItemFromBasket(basketItem)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('band.band') }}</th>
|
||||
<th>{{ $t('concert.concert') }}</th>
|
||||
<th class="text-center">{{ $t('misc.quantity') }}</th>
|
||||
<th class="text-right">{{ $t('misc.price') }}</th>
|
||||
<th class="text-right">{{ $t('misc.totalPrice') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr v-for="basketItem in basketStore.itemsInBasket">
|
||||
<!-- Band name -->
|
||||
<td>
|
||||
{{ basketItem.band.name }}
|
||||
</td>
|
||||
|
||||
<!-- Concert name -->
|
||||
<td>
|
||||
{{ basketItem.concert.name }}
|
||||
</td>
|
||||
|
||||
<!-- Quantity -->
|
||||
<td class="text-center">
|
||||
{{ basketItem.seats.length }}x
|
||||
</td>
|
||||
|
||||
<!-- Price per event -->
|
||||
<td class="text-right">
|
||||
<div v-if="basketItem.seats">
|
||||
{{ basketItem.price }} €
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Total price -->
|
||||
<td class="text-right">
|
||||
{{ (calcPrice(basketItem.concert.price, basketItem.seats.length)).toFixed(2) }} €
|
||||
</td>
|
||||
|
||||
<td class="text-right">
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
@click="removeFromBasket(basketItem)"
|
||||
color="red"
|
||||
variant="text"
|
||||
flat
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
</template>
|
||||
17
src/pages/misc/errorPage/index.vue
Normal file
17
src/pages/misc/errorPage/index.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
console.log("Error Page")
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-empty-state
|
||||
:headline="$t('misc.404.headline')"
|
||||
:title="$t('misc.404.title')"
|
||||
icon="mdi-robot-dead"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
109
src/pages/misc/helpPage/index.vue
Normal file
109
src/pages/misc/helpPage/index.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<script setup lang="ts">
|
||||
import { useExerciseStore } from '@/stores/exercise.store';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { generateResultsPdf } from '@/scripts/pdfScripts';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { LanguageEnum } from '@/data/enums/languageEnum';
|
||||
|
||||
const exerciseStore = useExerciseStore()
|
||||
const preferencesStore = usePreferencesStore()
|
||||
|
||||
exerciseStore.solveExercise(1, 1)
|
||||
|
||||
function getDotColor(exerciseGroupNr: number) {
|
||||
switch(exerciseGroupNr) {
|
||||
case 0: return "purple"
|
||||
case 1: return "orange"
|
||||
case 2: return "blue"
|
||||
case 3: return "pink"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container max-width="1000">
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col
|
||||
v-if="preferencesStore.studentName.length < 3 || preferencesStore.registrationNumber.length < 7"
|
||||
cols="auto"
|
||||
>
|
||||
<card-view variant="outlined" >
|
||||
{{ $t('misc.fulfillYourPersonalDataFirst') }}
|
||||
</card-view>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="auto">
|
||||
<v-tooltip :text="$t('misc.fulfillYourPersonalDataFirst')">
|
||||
<template #activator="{ props }"></template>
|
||||
|
||||
</v-tooltip>
|
||||
<outlined-button
|
||||
prepend-icon="mdi-file-pdf-box"
|
||||
@click="generateResultsPdf()"
|
||||
:disabled="preferencesStore.studentName.length < 3 || preferencesStore.registrationNumber.length < 7"
|
||||
>
|
||||
PDF generieren
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<card-view
|
||||
:title="$t('misc.firstStartup.exercises')"
|
||||
icon="mdi-checkbox-marked-circle-auto-outline"
|
||||
>
|
||||
<template #borderless>
|
||||
<v-timeline
|
||||
side="end"
|
||||
class="px-5"
|
||||
align="start"
|
||||
>
|
||||
<template v-for="exercise of exerciseStore.exercises">
|
||||
<v-timeline-item v-if="exercise.exerciseNr == 1"
|
||||
dot-color="grey"
|
||||
fill-dot
|
||||
>
|
||||
<div
|
||||
:class="`pt-1 text-h5 font-weight-bold text-${getDotColor(exercise.exerciseGroup.groupNr)}`"
|
||||
>
|
||||
{{
|
||||
(preferencesStore.language == LanguageEnum.GERMAN
|
||||
? exercise.exerciseGroup.nameDe
|
||||
: exercise.exerciseGroup.nameEn)
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{
|
||||
(preferencesStore.language == LanguageEnum.GERMAN
|
||||
? exercise.exerciseGroup.descriptionDe
|
||||
: exercise.exerciseGroup.descriptionEn)
|
||||
}}
|
||||
</div>
|
||||
</v-timeline-item>
|
||||
|
||||
<v-timeline-item
|
||||
:dot-color="getDotColor(exercise.exerciseGroup.groupNr)"
|
||||
:icon="exercise.solved ? 'mdi-check' : 'mdi-pencil'"
|
||||
>
|
||||
<!-- Right side -->
|
||||
<card-view
|
||||
:title="$t('help.scoreBoard.exerciseNr', [exercise.exerciseGroup.groupNr, exercise.exerciseNr]) +
|
||||
(preferencesStore.language == LanguageEnum.GERMAN ? exercise.nameDe : exercise.nameEn)"
|
||||
:color="exercise.solved ? 'green' : 'primary'"
|
||||
>
|
||||
{{ preferencesStore.language == LanguageEnum.GERMAN ? exercise.descriptionDe : exercise.descriptionEn }}
|
||||
</card-view>
|
||||
</v-timeline-item>
|
||||
</template>
|
||||
</v-timeline>
|
||||
</template>
|
||||
</card-view>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
65
src/pages/misc/homePage/bandsSection.vue
Normal file
65
src/pages/misc/homePage/bandsSection.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
import { useBandStore } from '@/stores/band.store';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const bandStore = useBandStore()
|
||||
const router = useRouter()
|
||||
|
||||
bandStore.getBands()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-slide-group
|
||||
show-arrows
|
||||
center-active
|
||||
>
|
||||
<v-slide-group-item
|
||||
v-for="band of bandStore.bands"
|
||||
v-slot="{ isSelected, toggle }"
|
||||
>
|
||||
<v-hover
|
||||
v-slot="{ isHovering, props }"
|
||||
>
|
||||
<v-card
|
||||
:class="{ 'on-hover': isHovering }"
|
||||
:elevation="isHovering ? 12 : 2"
|
||||
v-bind="props"
|
||||
class="mx-2"
|
||||
@click="router.push('bands/details/' + band.name.replaceAll(' ', '-').toLowerCase())"
|
||||
>
|
||||
<v-img
|
||||
class="d-flex align-center text-center"
|
||||
:src="band.imageMembers"
|
||||
height="250px"
|
||||
width="350"
|
||||
cover
|
||||
>
|
||||
<v-card-title class="text-white text-h5">
|
||||
<p class="mt-4">
|
||||
{{ band.name }}
|
||||
</p>
|
||||
|
||||
<p class="ma-0 text-body-1 font-weight-bold">
|
||||
{{ band.genres[0].name }}
|
||||
</p>
|
||||
</v-card-title>
|
||||
</v-img>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</v-slide-group-item>
|
||||
</v-slide-group>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.v-card {
|
||||
transition: opacity .4s ease-in-out;
|
||||
}
|
||||
|
||||
.v-card:not(.on-hover) {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.show-btns {
|
||||
color: rgba(255, 255, 255, 1) !important;
|
||||
}
|
||||
</style>
|
||||
45
src/pages/misc/homePage/index.vue
Normal file
45
src/pages/misc/homePage/index.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import { useConcertStore } from '@/stores/concert.store';
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
import bandSection from './bandsSection.vue';
|
||||
import UpcomingConcertsSection from './upcomingConcertsSection.vue';
|
||||
import TopLocationsSection from './topLocationsSection.vue';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
import welcomeDialog from './welcomeDialog.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const concertStore = useConcertStore()
|
||||
const locationStore = useLocationStore()
|
||||
const preferencesStore = usePreferencesStore()
|
||||
const showWelcomeDialog = ref(false)
|
||||
|
||||
concertStore.getUpcomingConcerts()
|
||||
locationStore.getTopLocations()
|
||||
|
||||
// First startup
|
||||
if (preferencesStore.firstStartup) {
|
||||
showWelcomeDialog.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pt-4">
|
||||
<band-section v-if="!preferencesStore.firstStartup" />
|
||||
</div>
|
||||
|
||||
<v-container v-if="!preferencesStore.firstStartup">
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col cols="10">
|
||||
<upcoming-concerts-section />
|
||||
|
||||
<top-locations-section />
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<welcome-dialog :model-value="showWelcomeDialog" />
|
||||
</template>
|
||||
58
src/pages/misc/homePage/topLocationsSection.vue
Normal file
58
src/pages/misc/homePage/topLocationsSection.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import cardViewTopImage from '@/components/basics/cardViewTopImage.vue';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useLocationStore } from '@/stores/location.store';
|
||||
|
||||
const router = useRouter()
|
||||
const locationStore = useLocationStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('location.topLocations')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col
|
||||
v-if="locationStore.fetchInProgress"
|
||||
v-for="n in 8"
|
||||
cols="6"
|
||||
md="3"
|
||||
>
|
||||
<card-view-top-image :loading="true" />
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
v-else
|
||||
v-for="location in locationStore.topLocations"
|
||||
cols="6"
|
||||
md="3"
|
||||
>
|
||||
<card-view-top-image
|
||||
:image="location.imageOutdoor"
|
||||
:title="location.name"
|
||||
smaller-title
|
||||
@click="router.push('/locations/details/' + location.name.replaceAll(' ', '-').toLowerCase())"
|
||||
:loading="locationStore.fetchInProgress"
|
||||
>
|
||||
{{ location.city.name }}, {{ location.city.country }}
|
||||
</card-view-top-image>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<outlined-button
|
||||
append-icon="mdi-chevron-right"
|
||||
@click="router.push('/locations')"
|
||||
block
|
||||
>
|
||||
{{ $t('location.allLocations') }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
61
src/pages/misc/homePage/upcomingConcertsSection.vue
Normal file
61
src/pages/misc/homePage/upcomingConcertsSection.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import { useConcertStore } from '@/stores/concert.store';
|
||||
import { useRouter } from 'vue-router';
|
||||
import cardViewTopImage from '@/components/basics/cardViewTopImage.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import moment from 'moment';
|
||||
|
||||
const concertStore = useConcertStore()
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('concert.upcomingConcerts')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col v-if="concertStore.fetchInProgress" v-for="n in 4" cols="6" md="3">
|
||||
<card-view-top-image :loading="true" />
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
v-else
|
||||
v-for="concert in concertStore.upcomingConcerts"
|
||||
cols="6"
|
||||
md="3"
|
||||
>
|
||||
<card-view-top-image
|
||||
:image="concert.image"
|
||||
:title="moment(concert.date).format('DD.MM.YYYY')"
|
||||
smaller-title
|
||||
@click="router.push('/bands/details/' + concert.band.name.replaceAll(' ', '-').toLowerCase())"
|
||||
:loading="concertStore.fetchInProgress"
|
||||
class="h-100"
|
||||
>
|
||||
<div>
|
||||
{{ concert.name }}
|
||||
</div>
|
||||
<div>
|
||||
{{ concert.band.name }}
|
||||
</div>
|
||||
{{ $t("misc.from") }} {{ (concert.price).toPrecision(4) }} €
|
||||
</card-view-top-image>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<outlined-button
|
||||
append-icon="mdi-chevron-right"
|
||||
@click="router.push('/concerts')"
|
||||
block
|
||||
>
|
||||
{{ $t('concert.allConcerts') }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
183
src/pages/misc/homePage/welcomeDialog.vue
Normal file
183
src/pages/misc/homePage/welcomeDialog.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<script setup lang="ts">
|
||||
import actionDialog from '@/components/basics/actionDialog.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import ServerStateText from '@/components/pageParts/serverStateText.vue';
|
||||
import { useFeedbackStore } from '@/stores/feedback.store';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const preferencesStore = usePreferencesStore()
|
||||
const feedbackStore = useFeedbackStore()
|
||||
const showDialog = defineModel()
|
||||
const currentStep = ref(0)
|
||||
|
||||
const steps = [
|
||||
feedbackStore.i18n.t('misc.firstStartup.connectToServer'),
|
||||
feedbackStore.i18n.t('misc.firstStartup.database'),
|
||||
feedbackStore.i18n.t('misc.firstStartup.exercises'),
|
||||
feedbackStore.i18n.t('misc.firstStartup.userData'),
|
||||
]
|
||||
|
||||
preferencesStore.getServerState()
|
||||
|
||||
|
||||
watch(() => currentStep.value, () => {
|
||||
switch(currentStep.value) {
|
||||
case 2: {
|
||||
preferencesStore.resetDb();
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
preferencesStore.resetExerciseProg();
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: {
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<action-dialog
|
||||
v-model="showDialog"
|
||||
:title="$t('misc.firstStartup.title')"
|
||||
icon="mdi-human-greeting"
|
||||
max-width="800"
|
||||
persistent
|
||||
>
|
||||
<v-stepper
|
||||
v-model="currentStep"
|
||||
alt-labels
|
||||
flat
|
||||
>
|
||||
<template #default="{ prev, next }">
|
||||
<!-- Header items -->
|
||||
<v-stepper-header>
|
||||
<template v-for="(step, n) in steps">
|
||||
<v-stepper-item
|
||||
:complete="currentStep > n + 1"
|
||||
:title="step"
|
||||
:value="n + 1"
|
||||
complete-icon="mdi-check"
|
||||
color="green"
|
||||
/>
|
||||
|
||||
<v-divider v-if="n < steps.length - 1" />
|
||||
</template>
|
||||
</v-stepper-header>
|
||||
|
||||
|
||||
<!-- Content -->
|
||||
<v-stepper-window>
|
||||
<v-stepper-window-item
|
||||
:value="1"
|
||||
class="text-h4 text-center"
|
||||
>
|
||||
<div>
|
||||
{{ $t('preferences.serverState') }}:
|
||||
</div>
|
||||
|
||||
<server-state-text />
|
||||
</v-stepper-window-item>
|
||||
|
||||
<v-stepper-window-item
|
||||
:value="2"
|
||||
>
|
||||
<div v-if="preferencesStore.fetchInProgress" class="text-center text-h4 pb-4">
|
||||
<div class="pb-4">
|
||||
{{ $t('misc.firstStartup.createDatabase') }}
|
||||
</div>
|
||||
|
||||
<v-progress-linear indeterminate />
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center text-h4 pb-4 text-green">
|
||||
<v-icon icon="mdi-check" /> {{ $t('misc.firstStartup.finished') }}
|
||||
</div>
|
||||
</v-stepper-window-item>
|
||||
|
||||
|
||||
<v-stepper-window-item
|
||||
:value="3"
|
||||
>
|
||||
<div v-if="preferencesStore.fetchInProgress" class="text-center text-h4 pb-4">
|
||||
<div class="pb-4">
|
||||
{{ $t('misc.firstStartup.createExercises') }}
|
||||
</div>
|
||||
|
||||
<v-progress-linear indeterminate />
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center text-h4 pb-4 text-green">
|
||||
<v-icon icon="mdi-check" /> {{ $t('misc.firstStartup.finished') }}
|
||||
</div>
|
||||
</v-stepper-window-item>
|
||||
|
||||
|
||||
<v-stepper-window-item
|
||||
:value="4"
|
||||
>
|
||||
<v-container class="px-0 py-2">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
hide-details
|
||||
:label="$t('misc.yourFullName')"
|
||||
v-model="preferencesStore.studentName"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
hide-details
|
||||
:label="$t('misc.registrationNumber')"
|
||||
v-model="preferencesStore.registrationNumber"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-stepper-window-item>
|
||||
</v-stepper-window>
|
||||
|
||||
|
||||
<!-- Next/Previous buttons -->
|
||||
<v-stepper-actions
|
||||
@click:next="next"
|
||||
>
|
||||
<template #prev="{ props }">
|
||||
<v-spacer />
|
||||
</template>
|
||||
|
||||
<template #next="{ props }">
|
||||
<outlined-button
|
||||
v-if="currentStep < 4"
|
||||
@click="props.onClick()"
|
||||
:disabled="preferencesStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('misc.actions.next') }}
|
||||
</outlined-button>
|
||||
|
||||
<outlined-button
|
||||
v-else
|
||||
@click="showDialog = false; preferencesStore.firstStartup = false"
|
||||
:disabled="preferencesStore.studentName.length == 0 ||
|
||||
preferencesStore.registrationNumber.length == 0"
|
||||
prepend-icon="mdi-check"
|
||||
color="green"
|
||||
>
|
||||
{{ $t('misc.firstStartup.complete') }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</v-stepper-actions>
|
||||
</template>
|
||||
</v-stepper>
|
||||
</action-dialog>
|
||||
</template>
|
||||
42
src/pages/misc/preferencesPage/aboutSection.vue
Normal file
42
src/pages/misc/preferencesPage/aboutSection.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import packageJson from './../../../../package.json'
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('preferences.aboutProject')"
|
||||
icon="mdi-information"
|
||||
>
|
||||
<template #borderless>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
title="Software Version"
|
||||
:subtitle="packageJson.version"
|
||||
prepend-icon="mdi-counter"
|
||||
/>
|
||||
<v-list-item
|
||||
title="Lizenz"
|
||||
subtitle="MIT"
|
||||
prepend-icon="mdi-license"
|
||||
/>
|
||||
<v-list-item
|
||||
title="Entwickler"
|
||||
subtitle="Tobias Zoghaib"
|
||||
prepend-icon="mdi-account"
|
||||
/>
|
||||
<v-list-item
|
||||
title="Entwickelt im Auftrag"
|
||||
subtitle="Uni Hannover, Institut für IT-Sicherheit, Fachgebiet Usable Security and Privacy"
|
||||
prepend-icon="mdi-school"
|
||||
/>
|
||||
<v-list-item
|
||||
title="Copyright"
|
||||
subtitle="2024"
|
||||
prepend-icon="mdi-copyright"
|
||||
/>
|
||||
</v-list>
|
||||
</template>
|
||||
</card-view>
|
||||
</template>
|
||||
34
src/pages/misc/preferencesPage/index.vue
Normal file
34
src/pages/misc/preferencesPage/index.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import pageSetup from './pageSetupSection.vue';
|
||||
import systemSetup from './systemSetupSection.vue';
|
||||
import aboutSection from './aboutSection.vue';
|
||||
import userSection from './userSection.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container max-width="800">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<page-setup />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<user-section />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<system-setup />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<about-section />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
39
src/pages/misc/preferencesPage/pageSetupSection.vue
Normal file
39
src/pages/misc/preferencesPage/pageSetupSection.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import { ThemeEnum } from '@/data/enums/themeEnums';
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
|
||||
const preferencesStore = usePreferencesStore()
|
||||
const themeEnums = Object.values(ThemeEnum)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('preferences.pageSetup')"
|
||||
icon="mdi-view-dashboard"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-select
|
||||
v-model="preferencesStore.theme"
|
||||
:items="themeEnums"
|
||||
:label="$t('preferences.selectedTheme')"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-select
|
||||
v-model="preferencesStore.language"
|
||||
:items="$i18n.availableLocales"
|
||||
:label="$t('preferences.language')"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</card-view>
|
||||
</template>
|
||||
91
src/pages/misc/preferencesPage/systemSetupSection.vue
Normal file
91
src/pages/misc/preferencesPage/systemSetupSection.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import confirmDialog from '@/components/basics/confirmDialog.vue';
|
||||
import { ServerStateEnum } from '@/data/enums/serverStateEnum';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
import ServerStateText from '@/components/pageParts/serverStateText.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const preferenceStore = usePreferencesStore()
|
||||
const router = useRouter()
|
||||
|
||||
preferenceStore.getServerState()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('preferences.systemSetup')"
|
||||
icon="mdi-engine"
|
||||
>
|
||||
<template #borderless>
|
||||
<v-list>
|
||||
<v-list-item class="text-h6 text-center">
|
||||
{{ $t('preferences.serverState') }}: <server-state-text />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="text-center">
|
||||
<outlined-button
|
||||
@click="preferenceStore.showDeleteDbDialog = true"
|
||||
prepend-icon="mdi-database-refresh"
|
||||
color="red"
|
||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('preferences.resetDatabase.resetDatabase') }}
|
||||
</outlined-button>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="text-center">
|
||||
<outlined-button
|
||||
@click="preferenceStore.showDeleteExerciseDialog = true"
|
||||
prepend-icon="mdi-progress-close"
|
||||
color="red"
|
||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('preferences.resetExerciseProgress.resetExerciseProgress') }}
|
||||
</outlined-button>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="text-center">
|
||||
<outlined-button
|
||||
@click="preferenceStore.showFactoryResetDialog = true"
|
||||
prepend-icon="mdi-factory"
|
||||
color="red"
|
||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||
>
|
||||
{{ $t('preferences.factoryReset.factoryReset') }}
|
||||
</outlined-button>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
</card-view>
|
||||
|
||||
<!-- Confirm delete database -->
|
||||
<confirm-dialog
|
||||
:title="$t('preferences.resetDatabase.dialog.title')"
|
||||
:description="$t('preferences.resetDatabase.dialog.description')"
|
||||
v-model="preferenceStore.showDeleteDbDialog"
|
||||
:onConfirm="preferenceStore.resetDb"
|
||||
:loading="preferenceStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
<!-- Confirm delete exercise progress -->
|
||||
<confirm-dialog
|
||||
:title="$t('preferences.resetExerciseProgress.dialog.title')"
|
||||
:description="$t('preferences.resetExerciseProgress.dialog.description')"
|
||||
v-model="preferenceStore.showDeleteExerciseDialog"
|
||||
:onConfirm="preferenceStore.resetExerciseProg"
|
||||
:loading="preferenceStore.fetchInProgress"
|
||||
/>
|
||||
|
||||
<confirm-dialog
|
||||
:title="$t('preferences.factoryReset.dialog.title')"
|
||||
:description="$t('preferences.factoryReset.dialog.description')"
|
||||
v-model="preferenceStore.showFactoryResetDialog"
|
||||
:onConfirm="() => {
|
||||
preferenceStore.resetToFactorySettings()
|
||||
router.push('/')
|
||||
}"
|
||||
:loading="preferenceStore.fetchInProgress"
|
||||
/>
|
||||
</template>
|
||||
32
src/pages/misc/preferencesPage/userSection.vue
Normal file
32
src/pages/misc/preferencesPage/userSection.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
|
||||
const preferencesStore = usePreferencesStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view icon="mdi-account-school" :title="$t('misc.user')">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
hide-details
|
||||
:label="$t('misc.yourFullName')"
|
||||
v-model="preferencesStore.studentName"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
hide-details
|
||||
:label="$t('misc.registrationNumber')"
|
||||
v-model="preferencesStore.registrationNumber"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</card-view>
|
||||
</template>
|
||||
150
src/pages/misc/searchPage/index.vue
Normal file
150
src/pages/misc/searchPage/index.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<script setup lang="ts">
|
||||
import searchBar from './searchBar.vue';
|
||||
import sectionDivider from '@/components/basics/sectionDivider.vue';
|
||||
import cardViewHorizontal from '@/components/basics/cardViewHorizontal.vue';
|
||||
import locationListItem from '@/components/pageParts/locationListItem.vue';
|
||||
import cardViewTopImage from '@/components/basics/cardViewTopImage.vue';
|
||||
import bandListItem from '@/components/pageParts/bandListItem.vue';
|
||||
import { useSearchStore } from '@/stores/search.store';
|
||||
import ConcertListItem from '@/components/pageParts/concertListItem.vue';
|
||||
|
||||
const searchStore = useSearchStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-spacer />
|
||||
|
||||
<v-col cols="10">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<search-bar />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<div v-if="searchStore.alreadySearched">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('band.band', 2)" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row
|
||||
v-if="searchStore.fetchInProgress"
|
||||
v-for="i in 2"
|
||||
>
|
||||
<v-col>
|
||||
<card-view-horizontal :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row
|
||||
v-else-if="searchStore.bands.length > 0"
|
||||
v-for="band in searchStore.bands">
|
||||
<v-col>
|
||||
<band-list-item
|
||||
:band="band"
|
||||
:concerts="band.concerts"
|
||||
:genres="band.genres"
|
||||
:loading="searchStore.fetchInProgress"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-else >
|
||||
<v-col>
|
||||
<v-empty-state
|
||||
:title="$t('band.noBandFound')"
|
||||
icon="mdi-guitar-electric"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
||||
|
||||
<!-- Section Concert results -->
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('concert.concert', 2)" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row
|
||||
v-if="searchStore.fetchInProgress"
|
||||
v-for="i in 2"
|
||||
>
|
||||
<v-col>
|
||||
<card-view-horizontal :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row
|
||||
v-else-if="searchStore.concerts.length > 0"
|
||||
v-for="concert in searchStore.concerts"
|
||||
>
|
||||
<v-col>
|
||||
<concert-list-item
|
||||
:concert="concert"
|
||||
:band="concert.band"
|
||||
:location="concert.location"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-else >
|
||||
<v-col>
|
||||
<v-empty-state
|
||||
:title="$t('concert.noConcertsFound')"
|
||||
icon="mdi-party-popper"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
||||
|
||||
<!-- Section Location results -->
|
||||
<v-row>
|
||||
<v-col>
|
||||
<section-divider :title="$t('location.location', 2)" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row
|
||||
v-if="searchStore.fetchInProgress"
|
||||
>
|
||||
<v-col v-for="i in 4">
|
||||
<card-view-top-image :loading="true" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row
|
||||
v-else-if="searchStore.locations.length > 0"
|
||||
>
|
||||
<v-col
|
||||
cols="6"
|
||||
md="3"
|
||||
v-for="location in searchStore.locations"
|
||||
>
|
||||
<location-list-item
|
||||
:location="location"
|
||||
:nr-of-concerts="location.concerts.length"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-else >
|
||||
<v-col>
|
||||
<v-empty-state
|
||||
:title="$t('location.noLocationsFound')"
|
||||
icon="mdi-city"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
26
src/pages/misc/searchPage/searchBar.vue
Normal file
26
src/pages/misc/searchPage/searchBar.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { useSearchStore } from '@/stores/search.store';
|
||||
|
||||
const searchStore = useSearchStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view >
|
||||
<v-text-field
|
||||
variant="outlined"
|
||||
hide-details
|
||||
v-model="searchStore.searchTerm"
|
||||
:placeholder="$t('misc.enterSomeKeywords')"
|
||||
@keyup.enter="searchStore.startSearch"
|
||||
>
|
||||
<template #append-inner>
|
||||
<v-btn
|
||||
icon="mdi-magnify"
|
||||
variant="plain"
|
||||
@click="searchStore.startSearch"
|
||||
/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</card-view>
|
||||
</template>
|
||||
Reference in New Issue
Block a user