Atomize model classes

This commit is contained in:
2024-10-11 17:42:21 +02:00
parent cfb8fb9d7d
commit 0ec11aacf7
59 changed files with 432 additions and 356 deletions

View File

@@ -15,12 +15,12 @@ city.get("/", (req: Request, res: Response) => {
] ]
}) })
.then(cities => { .then(cities => {
for (let city of cities) { // for (let city of cities) {
for (let location of city.dataValues.locations) { // for (let location of city.dataValues.locations) {
location.dataValues.nrOfConcerts = location.dataValues.concerts.length // location.dataValues.nrOfConcerts = location.dataValues.concerts.length
delete location.dataValues.concerts // delete location.dataValues.concerts
} // }
} // }
res.status(200).json(cities) res.status(200).json(cities)
}) })
}) })

View File

@@ -20,7 +20,12 @@ location.get("/", (req: Request, res: Response) => {
City, City,
{ {
model: Concert, model: Concert,
include: [ Event ], include: [
{
model: Event,
include: [ Band ]
}
],
attributes: { attributes: {
exclude: [ "locationId", "tourId" ] exclude: [ "locationId", "tourId" ]
} }
@@ -40,18 +45,6 @@ location.get("/", (req: Request, res: Response) => {
} }
}) })
.then(async locations => { .then(async locations => {
for (let location of locations) {
for (let concert of location.dataValues.concerts) {
let event = concert.dataValues.event
await Band.findByPk(event.dataValues.bandId)
.then(band => {
event.dataValues.bandName = band.dataValues.name
delete event.dataValues.bandId
})
}
}
if (sort != undefined) { if (sort != undefined) {
locations.sort((location1, location2) => { locations.sort((location1, location2) => {
if (sort == "desc") { if (sort == "desc") {
@@ -77,7 +70,12 @@ location.get("/location/:urlName", (req: Request, res: Response) => {
City, City,
{ {
model: Concert, model: Concert,
include: [ Event ], include: [
{
model: Event,
include: [ Band ]
}
],
attributes: { attributes: {
exclude: [ "locationId", "tourId" ] exclude: [ "locationId", "tourId" ]
} }
@@ -97,16 +95,6 @@ location.get("/location/:urlName", (req: Request, res: Response) => {
} }
}) })
.then(async location => { .then(async location => {
for (let concert of location.dataValues.concerts) {
let event = concert.dataValues.event
await Band.findByPk(event.dataValues.bandId)
.then(band => {
event.dataValues.bandName = band.dataValues.name
delete event.dataValues.bandId
})
}
for (let seatGroup of location.dataValues.seatGroups) { for (let seatGroup of location.dataValues.seatGroups) {
for (let seatRow of seatGroup.dataValues.seatRows) { for (let seatRow of seatGroup.dataValues.seatRows) {
for (let seat of seatRow.dataValues.seats) { for (let seat of seatRow.dataValues.seats) {

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { getAllExerciseGroups, updateExercise } from '@/data/api/exerciseApi'; import { updateExercise } from '@/data/api/exerciseApi';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';

View File

@@ -1,22 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import cardViewHorizontal from '@/components/basics/cardViewHorizontal.vue'; import cardViewHorizontal from '@/components/basics/cardViewHorizontal.vue';
import { ConcertModel } from '@/data/models/acts/concertModel';
defineProps({ defineProps({
/** Date of the concert */ /** Concert to display */
date: String, concert: {
type: ConcertModel,
required: true
},
/** Card title */ /** Card title */
title: String, title: String,
/** Name of the event */
eventName: String,
/** Price of the cheapest ticket option */
price: Number,
/** Number of available tickets, important to mark a concert as "sold out" */
inStock: Number,
/** Display text parts as skeleton */ /** Display text parts as skeleton */
loading: Boolean, loading: Boolean,
@@ -35,21 +30,21 @@ defineProps({
<card-view-horizontal <card-view-horizontal
:title="title" :title="title"
v-if="!loading" v-if="!loading"
:link="showButton && inStock > 0" :link="showButton && concert.inStock > 0"
@click="showButton && inStock > 0 ? onClick() : () => {}" @click="showButton && concert.inStock > 0 ? onClick() : () => {}"
> >
<template #prepend> <template #prepend>
<div> <div>
<div class="text-h4"> <div class="text-h4">
{{ String(new Date(date).getDate()).padStart(2, "0") }} {{ String(new Date(concert.date).getDate()).padStart(2, "0") }}
</div> </div>
<div class="text-h6"> <div class="text-h6">
{{ new Date(date).toLocaleString('default', { month: 'long' }) }} {{ new Date(concert.date).toLocaleString('default', { month: 'long' }) }}
</div> </div>
<div class="text-h6"> <div class="text-h6">
{{ new Date(date).getFullYear() }} {{ new Date(concert.date).getFullYear() }}
</div> </div>
</div> </div>
</template> </template>
@@ -61,10 +56,10 @@ defineProps({
<template #append> <template #append>
<div> <div>
<div class="text-secondary font-weight-medium text-h6 pb-1"> <div class="text-secondary font-weight-medium text-h6 pb-1">
{{ $t('from') + ' ' + price.toFixed(2) + ' €' }} {{ $t('from') + ' ' + concert.price.toFixed(2) + ' €' }}
</div> </div>
<div v-if="inStock == 0 && showButton" class="text-h6"> <div v-if="concert.inStock == 0 && showButton" class="text-h6">
{{ $t('soldOut') }} {{ $t('soldOut') }}
</div> </div>

View File

@@ -3,12 +3,29 @@ import { createDateRangeString, lowestTicketPrice } from '@/scripts/concertScrip
import cardViewHorizontal from '@/components/basics/cardViewHorizontal.vue'; import cardViewHorizontal from '@/components/basics/cardViewHorizontal.vue';
import { EventModel } from '@/data/models/acts/eventModel'; import { EventModel } from '@/data/models/acts/eventModel';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { BandModel } from '@/data/models/acts/bandModel';
import { ConcertModel } from '@/data/models/acts/concertModel';
const router = useRouter() const router = useRouter()
defineProps({ defineProps({
/** Event to display */ /** Event to display */
event: EventModel, event: {
type: EventModel,
required: true
},
/** Band which plays the event */
band: {
type: BandModel,
required: true
},
/** All concerts in the event */
concerts: {
type: Array<ConcertModel>,
required: true
},
/** Display text parts as skeleton */ /** Display text parts as skeleton */
loading: Boolean loading: Boolean
@@ -18,31 +35,31 @@ defineProps({
<template> <template>
<card-view-horizontal <card-view-horizontal
v-if="!loading" v-if="!loading"
:title="event.band.name + ' - ' + event.name" :title="band.name + ' - ' + event.name"
:image="'http://localhost:3000/static/' + event.image" :image="'http://localhost:3000/static/' + event.image"
@click="router.push('/bands/' + event.band.name.replaceAll(' ', '-').toLowerCase())" @click="router.push('/bands/' + band.name.replaceAll(' ', '-').toLowerCase())"
> >
<template #content> <template #content>
<div class="oneLine my-2 pr-4 text-disabled" > <div class="oneLine my-2 pr-4 text-disabled" >
{{ event.band.descriptionDe }} {{ band.descriptionDe }}
<!-- todo: Englisch text --> <!-- todo: Englisch text -->
</div> </div>
<div class="text-disabled oneLine"> <div class="text-disabled oneLine">
{{ createDateRangeString(event) }} - {{ event.concerts.length }} {{ createDateRangeString(concerts) }} - {{ concerts.length }}
{{ $t('concert', event.concerts.length) }} {{ $t('concert', concerts.length) }}
</div> </div>
</template> </template>
<template #append> <template #append>
<div> <div>
<div class="text-secondary font-weight-medium text-h6 pb-1"> <div class="text-secondary font-weight-medium text-h6 pb-1">
{{ $t('from') + ' ' + lowestTicketPrice(event) + ' €' }} {{ $t('from') + ' ' + lowestTicketPrice(concerts) + ' €' }}
</div> </div>
<div> <div>
<v-btn variant="flat" color="secondary"> <v-btn variant="flat" color="secondary">
{{ event.concerts.length }} {{ $t('concert', event.concerts.length) }} {{ concerts.length }} {{ $t('concert', concerts.length) }}
</v-btn> </v-btn>
</div> </div>
</div> </div>

View File

@@ -1,12 +1,21 @@
<script setup lang="ts"> <script setup lang="ts">
import { useFeedbackStore } from '@/data/stores/feedbackStore';
defineProps({ defineProps({
/** Background image */
image: String, image: String,
/** Mini image on the left */
logo: String, logo: String,
/** Title */
title: String, title: String,
/** Array of string to display as chips */
chips: Array<String>, chips: Array<String>,
/** Description text */
description: String, description: String,
/** If true, display skeleton loader */
loading: Boolean loading: Boolean
}) })
</script> </script>
@@ -21,6 +30,7 @@ defineProps({
> >
<div class="position-absolute bottom-0 pa-5" style="width: 100%;"> <div class="position-absolute bottom-0 pa-5" style="width: 100%;">
<v-row> <v-row>
<!-- Logo -->
<v-col cols="2"> <v-col cols="2">
<v-skeleton-loader <v-skeleton-loader
type="image" type="image"
@@ -40,19 +50,24 @@ defineProps({
</v-skeleton-loader> </v-skeleton-loader>
</v-col> </v-col>
<v-col cols="8"> <v-col cols="8">
<!-- Title -->
<v-skeleton-loader <v-skeleton-loader
type="heading" type="heading"
:loading="loading" :loading="loading"
width="500" width="500"
> >
<span class="text-h3">{{ title }}</span> <span class="text-h3 font-weight-bold">
{{ title }}
</span>
</v-skeleton-loader> </v-skeleton-loader>
<v-skeleton-loader <v-skeleton-loader
:loading="loading" :loading="loading"
type="sentences" type="sentences"
> >
<!-- Chips -->
<v-chip <v-chip
v-for="chip in chips" v-for="chip in chips"
class="mr-2 my-1" class="mr-2 my-1"
@@ -61,9 +76,13 @@ defineProps({
{{ chip }} {{ chip }}
</v-chip> </v-chip>
<p class="text-h6" v-if="!$slots.description">{{ description }}</p>
<!-- Description -->
<p class="text-h6 text-medium-emphasis" v-if="!$slots.description">
{{ description }}
</p>
<p class="text-h6"> <p class="text-h6 text-medium-emphasis">
<slot name="description"></slot> <slot name="description"></slot>
</p> </p>
</v-skeleton-loader> </v-skeleton-loader>

View File

@@ -1,12 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import cardViewTopImage from '../basics/cardViewTopImage.vue'; import cardViewTopImage from '../basics/cardViewTopImage.vue';
import { LocationModel } from '@/data/models/locations/locationModel';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { LocationModel } from '@/data/models/locations/locationModel';
import { ConcertModel } from '@/data/models/acts/concertModel';
const router = useRouter() const router = useRouter()
defineProps({ defineProps({
location: LocationModel location: {
type: LocationModel,
required: true
},
concerts: {
type: Array<ConcertModel>,
required: true
}
}) })
</script> </script>
@@ -17,7 +25,7 @@ defineProps({
@click="router.push('locations/' + location.name.replaceAll(' ', '-').toLowerCase())" @click="router.push('locations/' + location.name.replaceAll(' ', '-').toLowerCase())"
> >
<div> <div>
{{ location.concerts.length }} {{ $t('concert', location.concerts.length) }} {{ concerts.length }} {{ $t('concert', concerts.length) }}
</div> </div>
</card-view-top-image> </card-view-top-image>
</template> </template>

View File

@@ -1,17 +1,41 @@
<script setup lang="ts"> <script setup lang="ts">
import { ConcertModel } from '@/data/models/acts/concertModel'; import { ConcertModel } from '@/data/models/acts/concertModel';
import cardWithLeftImage from '../basics/cardViewHorizontal.vue'; import cardWithLeftImage from '../basics/cardViewHorizontal.vue';
import { dateStringToHumanReadableString, dateToHumanReadableString } from '@/scripts/dateTimeScripts'; import { dateStringToHumanReadableString } from '@/scripts/dateTimeScripts';
import { EventModel } from '@/data/models/acts/eventModel';
import { BandModel } from '@/data/models/acts/bandModel';
import { LocationModel } from '@/data/models/locations/locationModel';
import { CityModel } from '@/data/models/locations/cityModel';
defineProps({ defineProps({
concert: ConcertModel, concert: {
type: ConcertModel,
required: true
},
event: {
type: EventModel,
required: true
},
band: {
type: BandModel,
required: true
},
location: {
type: LocationModel,
required: true
},
city: {
type: CityModel,
required: true
},
/** Image to print on the left side */ /** Image to print on the left side */
image: String, image: String,
/** Event series name */
eventName: String,
seatGroup: String, seatGroup: String,
seatRow: Number, seatRow: Number,
@@ -30,7 +54,7 @@ defineProps({
:image="'http://localhost:3000/static/' + image" :image="'http://localhost:3000/static/' + image"
:link="false" :link="false"
color-header="primary" color-header="primary"
:title="concert.event.band.name + ' - ' + concert.event.name" :title="band.name + ' - ' + event.name"
> >
<template #content> <template #content>
<div class="text-caption"> <div class="text-caption">
@@ -46,7 +70,7 @@ defineProps({
</div> </div>
<div> <div>
{{ concert.location.name }}, {{ concert.location.city.name }} {{ location.name }}, {{ city.name }}
</div> </div>
<div class="text-caption"> <div class="text-caption">

View File

@@ -2,10 +2,10 @@
import { SeatGroupModel } from '@/data/models/locations/seatGroupModel'; import { SeatGroupModel } from '@/data/models/locations/seatGroupModel';
import seatGroupSheet from './seatGroupSheet.vue'; import seatGroupSheet from './seatGroupSheet.vue';
import { ConcertModel } from '@/data/models/acts/concertModel'; import { ConcertModel } from '@/data/models/acts/concertModel';
import { LocationModel } from '@/data/models/locations/locationModel'; import { LocationApiModel } from '@/data/models/locations/locationApiModel';
let props = defineProps({ let props = defineProps({
location: LocationModel, location: LocationApiModel,
concert: { concert: {
type: ConcertModel, type: ConcertModel,
default: new ConcertModel() default: new ConcertModel()

View File

@@ -1,5 +1,5 @@
import axios from "axios" import axios from "axios"
import { AccountModel } from "../models/accountModel" import { AccountModel } from "../models/user/accountModel"
const BASE_URL = "http://localhost:3000/accounts" const BASE_URL = "http://localhost:3000/accounts"

View File

@@ -2,6 +2,14 @@ import axios from "axios"
const BASE_URL = "http://localhost:3000/events" const BASE_URL = "http://localhost:3000/events"
/**
* Request all events in the database
*
* @param city Filter by name of city where the concert is
* @param genre Filter by genre of the band
*
* @returns All events which fulfill the params
*/
export async function fetchEvents(city: string = "", genre: string = "") { export async function fetchEvents(city: string = "", genre: string = "") {
let url = BASE_URL + "?" let url = BASE_URL + "?"
url += (city.length > 0) ? "city=" + city + "&" : "" url += (city.length > 0) ? "city=" + city + "&" : ""
@@ -10,7 +18,14 @@ export async function fetchEvents(city: string = "", genre: string = "") {
return await axios.get(url) return await axios.get(url)
} }
export async function getTopEvents(nrOfEvents) { /**
* Returns events with the most concerts
*
* @param nrOfEvents Limit number of returned objects
*
* @returns Limited number of objects with the most concerts in it
*/
export async function getTopEvents(nrOfEvents: number) {
let url = BASE_URL + "?sort=desc&count=" + nrOfEvents let url = BASE_URL + "?sort=desc&count=" + nrOfEvents
return await axios.get(url) return await axios.get(url)

View File

@@ -1,6 +1,5 @@
import axios from "axios" import axios from "axios"
import { OrderModel } from "../models/orderModel" import { BasketItemModel } from "../models/ordering/basketItemModel"
import { BasketItemModel } from "../models/basketItemModel"
const BASE_URL = "http://localhost:3000/orders" const BASE_URL = "http://localhost:3000/orders"

View File

@@ -0,0 +1,12 @@
import { BandModel } from "./bandModel";
import { EventApiModel } from "./eventApiModel";
import { GenreModel } from "./genreModel"
import { MemberModel } from "./memberModel"
import { RatingModel } from "./ratingModel"
export class BandApiModel extends BandModel {
ratings: Array<RatingModel> = []
members: Array<MemberModel> = []
genres: Array<GenreModel> = []
events: Array<EventApiModel> = []
}

View File

@@ -1,22 +1,10 @@
import { EventModel } from "./eventModel"
import { RatingModel } from "./ratingModel"
export class BandModel { export class BandModel {
id: number id: number = -1
name: string name: string = ""
foundingYear: number foundingYear: number = 1900
descriptionEn: string descriptionEn: string = ""
descriptionDe: string descriptionDe: string = ""
images: Array<string> images: Array<string> = []
imageMembers: string imageMembers: string = ""
logo: string logo: string = ""
ratings: Array<RatingModel> = []
members: Array<{
name: string,
image: string
}>
genres: Array<{
name: string
}> = []
events: Array<EventModel>
} }

View File

@@ -0,0 +1,8 @@
import { LocationApiModel } from "../locations/locationApiModel"
import { ConcertModel } from "./concertModel"
import { EventApiModel } from "./eventApiModel"
export class ConcertApiModel extends ConcertModel {
location: LocationApiModel = new LocationApiModel()
event: EventApiModel = new EventApiModel()
}

View File

@@ -1,24 +1,6 @@
import { LocationModel } from "./../locations/locationModel"
import { BandModel } from "./bandModel"
import { EventModel } from "./eventModel"
export class ConcertModel { export class ConcertModel {
id: number = 0 id: number = -1
inStock: number = 0 inStock: number = 0
date: string = "" date: string = ""
price: number = 0 price: number = 0
location: LocationModel = new LocationModel()
event: {
id: number
name: string
offered: boolean
image: string
band: BandModel
} = {
id: 0,
name: "",
offered: true,
image: "",
band: new BandModel()
}
} }

View File

@@ -0,0 +1,8 @@
import { EventModel } from "./eventModel";
import { BandModel } from "./bandModel"
import { ConcertApiModel } from "./concertApiModel";
export class EventApiModel extends EventModel {
concerts: Array<ConcertApiModel> = []
band: BandModel = new BandModel()
}

View File

@@ -1,11 +1,6 @@
import { BandModel } from "./bandModel"
import { ConcertModel } from "./concertModel"
export class EventModel { export class EventModel {
id: number id: number = -1
name: string name: string = ""
offered: boolean offered: boolean = true
image: string image: string = ""
band: BandModel = new BandModel()
concerts: Array<ConcertModel> = [ new ConcertModel() ]
} }

View File

@@ -0,0 +1,6 @@
import { GenreModel } from "./genreModel";
import { BandModel } from "./bandModel"
export class GenreApiModel extends GenreModel {
bands: Array<BandModel>
}

View File

@@ -1,14 +1,4 @@
import { RatingModel } from "./ratingModel"
export class GenreModel { export class GenreModel {
id: number id: number = -1
name: string name: string = ""
bands: Array<
{
name: string
images: Array<string>
logo: string
ratings: Array<RatingModel>
}
>
} }

View File

@@ -0,0 +1,4 @@
export class MemberModel {
name: string = ""
image: string = ""
}

View File

@@ -1,7 +1,4 @@
import { BandModel } from "./bandModel"
export class RatingModel { export class RatingModel {
id: number id: number = -1
rating: number rating: number = 1
band: BandModel
} }

View File

@@ -1,14 +0,0 @@
import { BandModel } from "./bandModel"
import { ConcertModel } from "./concertModel"
/**
* @deprecated Use EventModel!
*/
export class TourModel {
id: number
name: string
offered: boolean
band: BandModel
image: string
concerts: Array<ConcertModel>
}

View File

@@ -0,0 +1,6 @@
import { ExerciseModel } from "./exerciseModel";
import { ExerciseGroupModel } from "./exerciseGroupModel";
export class ExerciseGroupApiModel extends ExerciseGroupModel {
exercises: Array<ExerciseModel>
}

View File

@@ -1,8 +1,6 @@
import { ExerciseModel } from "./exerciseModel"
export class ExerciseGroupModel { export class ExerciseGroupModel {
id = -1
nameDe: string = "" nameDe: string = ""
nameEn: string = "" nameEn: string = ""
groupNr: number = 0 groupNr: number = 0
exercises: Array<ExerciseModel>
} }

View File

@@ -1,4 +1,5 @@
export class ExerciseModel { export class ExerciseModel {
id = -1
nameDe: string = "" nameDe: string = ""
nameEn: string = "" nameEn: string = ""
exerciseNr: number = 0 exerciseNr: number = 0

View File

@@ -0,0 +1,12 @@
import { LocationApiModel } from "./locationApiModel"
/**
* Replica of the API endpoint /cities
*/
export class CityApiModel {
id: number = -1
name: string = ""
country: string = ""
image: string = ""
locations: Array<LocationApiModel>
}

View File

@@ -1,24 +1,6 @@
/**
* Replica of the API endpoint /cities
*/
export class CityModel { export class CityModel {
id: number = -1 id: number = -1
name: string = "" name: string = ""
country: string = "" country: string = ""
image: string = "" image: string = ""
locations: Array<{
id: number
name: string
address: string
imageIndoor: string
imageOutdoor: string
nrOfConcerts: number
}> = [{
id: -1,
name: "",
address: "",
imageIndoor: "",
imageOutdoor: "",
nrOfConcerts: 0
}]
} }

View File

@@ -0,0 +1,13 @@
import { ConcertApiModel } from "../acts/concertApiModel"
import { CityModel } from "./cityModel"
import { LocationModel } from "./locationModel"
import { SeatGroupModel } from "./seatGroupModel"
/**
* Replica of the API endpoint /locations
*/
export class LocationApiModel extends LocationModel {
city: CityModel = new CityModel()
concerts: Array<ConcertApiModel> = []
seatGroups: Array<SeatGroupModel> = []
}

View File

@@ -1,32 +1,8 @@
import { SeatGroupModel } from "./seatGroupModel"
/**
* Replica of the API endpoint /locations
*/
export class LocationModel { export class LocationModel {
id: number id: number = -1
name: string name: string = ""
address: string address: string = ""
imageIndoor: string imageIndoor: string = ""
imageOutdoor: string imageOutdoor: string = ""
seatSchema: string layout: number = 1
layout: number
city: {
name: string
country: string
} = { name: "", country: "" }
concerts: Array<{
id: number
date: string
price: number
inStock: number
location: string
event: {
name: string
offered: boolean
image: string
bandName: string
}
}>
seatGroups: Array<SeatGroupModel>
} }

View File

@@ -1,9 +1,9 @@
import { SeatRowModel } from "./seatRowModel" import { SeatRowModel } from "./seatRowModel"
export class SeatGroupModel { export class SeatGroupModel {
name: string name: string = ""
surcharge: number surcharge: number = 0
standingArea: Boolean standingArea: Boolean = false
capacity: number capacity: number = 0
seatRows: Array<SeatRowModel> seatRows: Array<SeatRowModel>
} }

View File

@@ -1,5 +1,5 @@
export class SeatModel { export class SeatModel {
id: number id: number = -1
seatNr: string seatNr: string = ""
state: number = 0 state: number = 0
} }

View File

@@ -1,6 +1,6 @@
import { SeatModel } from "./seatModel" import { SeatModel } from "./seatModel"
export class SeatRowModel { export class SeatRowModel {
row: number row: number = 0
seats: Array<SeatModel> seats: Array<SeatModel>
} }

View File

@@ -1,13 +1,20 @@
import { BandApiModel } from "../acts/bandApiModel"
import { BandModel } from "../acts/bandModel"
import { ConcertModel } from "../acts/concertModel" import { ConcertModel } from "../acts/concertModel"
import { EventModel } from "../acts/eventModel"
import { SeatModel } from "../locations/seatModel" import { SeatModel } from "../locations/seatModel"
export class BasketItemModel { export class BasketItemModel {
concert: ConcertModel = new ConcertModel() concert: ConcertModel
event: EventModel
band: BandModel = new BandModel()
seats: Array<SeatModel> = [] seats: Array<SeatModel> = []
price: number price: number
constructor(concert: ConcertModel, seat: SeatModel, price: number) { constructor(concert: ConcertModel, event: EventModel, band: BandModel, seat: SeatModel, price: number) {
this.concert = concert this.concert = concert
this.event = event
this.band = band
this.seats = [ seat ] this.seats = [ seat ]
this.price = price this.price = price
} }

View File

@@ -0,0 +1,12 @@
import { AccountModel } from "../user/accountModel"
import { AddressModel } from "../user/addressModel"
import { PaymentModel } from "../user/paymentModel"
import { OrderModel } from "./orderModel"
import { TicketApiModel } from "./ticketApiModel"
export class OrderApiModel extends OrderModel {
tickets: Array<TicketApiModel>
account: AccountModel
payment: PaymentModel
address: AddressModel
}

View File

@@ -1,13 +1,5 @@
import { AddressModel } from "../user/addressModel"
import { PaymentModel } from "../user/paymentModel"
import { TicketModel } from "./ticketModel"
export class OrderModel { export class OrderModel {
id: number id: number
accountId: number
shippingProgress: number shippingProgress: number
tickets: Array<TicketModel>
orderedAt: string orderedAt: string
payment: PaymentModel
address: AddressModel
} }

View File

@@ -0,0 +1,8 @@
import { ConcertApiModel } from "../acts/concertApiModel";
import { SeatModel } from "../locations/seatModel";
import { TicketModel } from "./ticketModel";
export class TicketApiModel extends TicketModel {
concert: ConcertApiModel
seat: SeatModel
}

View File

@@ -1,20 +1,5 @@
import { ConcertModel } from "../acts/concertModel"
import { SeatModel } from "../locations/seatModel"
export class TicketModel { export class TicketModel {
id: number id: number
orderId: number = -1 orderId: number = -1
orderPrice: number = 0 orderPrice: number = 0
concert: ConcertModel
seat: {
seatNr: number,
seatRow: {
row: number,
seatGroup: {
name: string,
surcharge: number,
standingArea: boolean
}
}
}
} }

View File

@@ -0,0 +1,10 @@
import { AccountModel } from "./accountModel"
import { AccountRole } from "./accountRole"
import { AddressModel } from "./addressModel"
import { PaymentModel } from "./paymentModel"
export class AccountApiModel extends AccountModel {
addresses: Array<AddressModel>
payments: Array<PaymentModel>
accountRole: AccountRole
}

View File

@@ -1,7 +1,3 @@
import { AccountRole } from "./accountRole"
import { AddressModel } from "./addressModel"
import { PaymentModel } from "./paymentModel"
export class AccountModel { export class AccountModel {
id: number id: number
username: string = "" username: string = ""
@@ -9,7 +5,4 @@ export class AccountModel {
email: string = "" email: string = ""
firstName: string = "" firstName: string = ""
lastName: string = "" lastName: string = ""
addresses: Array<AddressModel> = [ new AddressModel() ]
payments: Array<PaymentModel> = [ new PaymentModel() ]
accountRole: AccountRole = new AccountRole()
} }

View File

@@ -8,10 +8,11 @@ import { getUserOrders } from "../api/orderApi";
import { BannerStateEnum } from "../enums/bannerStateEnum"; import { BannerStateEnum } from "../enums/bannerStateEnum";
import { AddressModel } from "../models/user/addressModel"; import { AddressModel } from "../models/user/addressModel";
import { PaymentModel } from "../models/user/paymentModel"; import { PaymentModel } from "../models/user/paymentModel";
import { AccountApiModel } from "../models/user/accountApiModel";
export const useAccountStore = defineStore("accountStore", { export const useAccountStore = defineStore("accountStore", {
state: () => ({ state: () => ({
userAccount: useLocalStorage("hackmycart/accountStore/userAccount", new AccountModel()) userAccount: useLocalStorage("hackmycart/accountStore/userAccount", new AccountApiModel())
}), }),
actions: { actions: {

View File

@@ -8,6 +8,8 @@ import { PaymentModel } from "../models/user/paymentModel";
import { ref } from "vue"; import { ref } from "vue";
import { SelectedSeatModel } from "../models/ordering/selectedSeatModel"; import { SelectedSeatModel } from "../models/ordering/selectedSeatModel";
import { calcPrice } from "@/scripts/concertScripts"; import { calcPrice } from "@/scripts/concertScripts";
import { EventModel } from "../models/acts/eventModel";
import { BandModel } from "../models/acts/bandModel";
export const useBasketStore = defineStore('basketStore', { export const useBasketStore = defineStore('basketStore', {
state: () => ({ state: () => ({
@@ -49,7 +51,7 @@ export const useBasketStore = defineStore('basketStore', {
) )
}, },
moveSeatSelectionsToBasket() { moveSeatSelectionsToBasket(event: EventModel, band: BandModel) {
for (let selectedSeat of this.selectedSeats) { for (let selectedSeat of this.selectedSeats) {
let itemInBasket: BasketItemModel = this.itemsInBasket.find((basketItem: BasketItemModel) => { let itemInBasket: BasketItemModel = this.itemsInBasket.find((basketItem: BasketItemModel) => {
return basketItem.concert.id == selectedSeat.concert.id return basketItem.concert.id == selectedSeat.concert.id
@@ -59,7 +61,13 @@ export const useBasketStore = defineStore('basketStore', {
itemInBasket.seats.push(selectedSeat.seat) itemInBasket.seats.push(selectedSeat.seat)
} else { } else {
this.itemsInBasket.push( this.itemsInBasket.push(
new BasketItemModel(selectedSeat.concert, selectedSeat.seat, selectedSeat.concert.price) new BasketItemModel(
selectedSeat.concert,
event,
band,
selectedSeat.seat,
selectedSeat.concert.price
)
) )
} }
} }

View File

@@ -1,18 +1,18 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { ref } from "vue"; import { ref } from "vue";
import { EventModel } from "../models/acts/eventModel";
import { fetchEvents } from "../api/eventApi"; import { fetchEvents } from "../api/eventApi";
import { fetchAllCities } from "../api/cityApi"; import { fetchAllCities } from "../api/cityApi";
import { CityModel } from "../models/locations/cityModel";
import { GenreModel } from "../models/acts/genreModel";
import { fetchAllGenres } from "../api/genreApi"; import { fetchAllGenres } from "../api/genreApi";
import { useFeedbackStore } from "./feedbackStore"; import { useFeedbackStore } from "./feedbackStore";
import { CityApiModel } from "../models/locations/cityApiModel";
import { EventApiModel } from "../models/acts/eventApiModel";
import { GenreApiModel } from "../models/acts/genreApiModel";
export const useShoppingStore = defineStore("shoppingStore", { export const useShoppingStore = defineStore("shoppingStore", {
state: () => ({ state: () => ({
events: ref<Array<EventModel>>([]), events: ref<Array<EventApiModel>>([]),
cities: ref<Array<CityModel>>([]), cities: ref<Array<CityApiModel>>([]),
genres: ref<Array<GenreModel>>([]), genres: ref<Array<GenreApiModel>>([]),
cityFilterName: ref<string>(), cityFilterName: ref<string>(),
genreFilterName: ref<string>() genreFilterName: ref<string>()
}), }),

View File

@@ -5,11 +5,11 @@ import { useRouter } from 'vue-router';
import outlinedButton from '@/components/basics/outlinedButton.vue'; import outlinedButton from '@/components/basics/outlinedButton.vue';
import { getUserOrders } from '@/data/api/orderApi'; import { getUserOrders } from '@/data/api/orderApi';
import { ref } from 'vue'; import { ref } from 'vue';
import { OrderModel } from '@/data/models/ordering/orderModel'; import { OrderApiModel } from '@/data/models/ordering/orderApiModel';
const accountStore = useAccountStore() const accountStore = useAccountStore()
const router = useRouter() const router = useRouter()
const orders = ref<Array<OrderModel>>([]) const orders = ref<Array<OrderApiModel>>([])
getUserOrders(accountStore.userAccount.id) getUserOrders(accountStore.userAccount.id)
.then(result => { .then(result => {
@@ -32,7 +32,9 @@ getUserOrders(accountStore.userAccount.id)
v-for="order in orders" v-for="order in orders"
> >
<v-col> <v-col>
<order-item :order="order" /> <order-item
:order="order"
/>
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -1,13 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import cardView from '@/components/basics/cardView.vue'; import cardView from '@/components/basics/cardView.vue';
import { OrderModel } from '@/data/models/ordering/orderModel';
import { useAccountStore } from '@/data/stores/accountStore';
import ticketListItem from '@/components/pageParts/ticketListItem.vue'; import ticketListItem from '@/components/pageParts/ticketListItem.vue';
import { OrderApiModel } from '@/data/models/ordering/orderApiModel';
const accountStore = useAccountStore()
defineProps({ defineProps({
order: OrderModel, order: OrderApiModel,
loading: { loading: {
type: Boolean, type: Boolean,
default: false default: false
@@ -56,12 +53,16 @@ function formatDateTimeString(string: string) {
<v-col> <v-col>
<ticket-list-item <ticket-list-item
:concert="ticket.concert" :concert="ticket.concert"
:event="ticket.concert.event"
:band="ticket.concert.event.band"
:location="ticket.concert.location"
:city="ticket.concert.location.city"
:image="ticket.concert.event.image" :image="ticket.concert.event.image"
:seat-group="ticket.seat.seatRow.seatGroup.name" />
<!-- todo :seat-group="ticket.seat.seatRow.seatGroup.name"
:seat-row="ticket.seat.seatRow.row" :seat-row="ticket.seat.seatRow.row"
:seat="ticket.seat.seatNr" :seat="ticket.seat.seatNr"
:standing-area="ticket.seat.seatRow.seatGroup.standingArea" :standing-area="ticket.seat.seatRow.seatGroup.standingArea" -->
/>
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -5,8 +5,6 @@ import orderingDialog from './orderingDialog.vue';
import outlinedButton from '@/components/basics/outlinedButton.vue'; import outlinedButton from '@/components/basics/outlinedButton.vue';
import { ref } from 'vue'; import { ref } from 'vue';
import { useAccountStore } from '@/data/stores/accountStore'; import { useAccountStore } from '@/data/stores/accountStore';
import concertListItem from '@/components/pageParts/concertListItem.vue';
import { dateStringToHumanReadableString } from '@/scripts/dateTimeScripts';
import ticketsTable from './ticketsTable.vue'; import ticketsTable from './ticketsTable.vue';
const basketStore = useBasketStore() const basketStore = useBasketStore()

View File

@@ -8,10 +8,6 @@ const basketStore = useBasketStore()
function removeFromBasket(basketItem: BasketItemModel) { function removeFromBasket(basketItem: BasketItemModel) {
basketStore.removeItemFromBasket(basketItem) basketStore.removeItemFromBasket(basketItem)
} }
function editQuantity(basketItem: BasketItemModel) {
// todo
}
</script> </script>
<template> <template>
@@ -31,12 +27,12 @@ function editQuantity(basketItem: BasketItemModel) {
<tr v-for="basketItem in basketStore.itemsInBasket"> <tr v-for="basketItem in basketStore.itemsInBasket">
<!-- Band name --> <!-- Band name -->
<td> <td>
{{ basketItem.concert.event.band.name }} {{ basketItem.band.name }}
</td> </td>
<!-- Event name --> <!-- Event name -->
<td> <td>
{{ basketItem.concert.event.name }} {{ basketItem.event.name }}
</td> </td>
<!-- Quantity --> <!-- Quantity -->

View File

@@ -1,13 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { BandModel } from '@/data/models/acts/bandModel';
import cardWithTopImage from '@/components/basics/cardViewTopImage.vue'; import cardWithTopImage from '@/components/basics/cardViewTopImage.vue';
import { useFeedbackStore } from '@/data/stores/feedbackStore'; import { useFeedbackStore } from '@/data/stores/feedbackStore';
import { BandApiModel } from '@/data/models/acts/bandApiModel';
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
defineProps({ defineProps({
band: { band: {
type: BandModel, type: BandApiModel,
required: true required: true
} }
}) })

View File

@@ -1,48 +1,48 @@
<script setup lang="ts"> <script setup lang="ts">
import { BandModel } from '@/data/models/acts/bandModel';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useFeedbackStore } from '@/data/stores/feedbackStore'; import { useFeedbackStore } from '@/data/stores/feedbackStore';
import concertListItem from '@/components/pageParts/concertListItem.vue'; import concertListItem from '@/components/pageParts/concertListItem.vue';
import { ConcertApiModel } from '@/data/models/acts/concertApiModel';
import { BandApiModel } from '@/data/models/acts/bandApiModel';
import { EventApiModel } from '@/data/models/acts/eventApiModel';
const router = useRouter() const router = useRouter()
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
defineProps({ defineProps({
band: { band: BandApiModel,
type: BandModel, events: Array<EventApiModel>
required: true
}
}) })
</script> </script>
<template> <template>
<v-row v-if="feedbackStore.fetchDataFromServerInProgress" v-for="i in 3"> <v-row v-if="feedbackStore.fetchDataFromServerInProgress" v-for="i in 3">
<v-col> <v-col>
<concert-list-item :loading="true" /> <!-- <concert-list-item
:loading="true" /> -->
</v-col> </v-col>
</v-row> </v-row>
<v-row v-else v-for="concert of band.events[0].concerts"> <div v-else v-for="event of events">
<v-col> <v-row v-for="concert of event.concerts">
<concert-list-item <v-col>
:date="concert.date" <concert-list-item
:price="concert.price" :concert="concert"
:title="concert.location.city.name" :title="concert.location.city.name"
:description="concert.location.name" :link="concert.inStock > 0"
:link="concert.inStock > 0" :onClick="() => router.push('/concert/' + concert.id)"
:in-stock="concert.inStock" >
:onClick="() => router.push('/concert/' + concert.id)" <template #description>
> <div>
<template #description> {{ concert.location.name }}
<div> </div>
{{ concert.location.name }}
</div>
<div> <div>
{{ band.name }} - {{ band.events[0].name }} {{ band.name }} - {{ band.events[0].name }}
</div> </div>
</template> </template>
</concert-list-item> </concert-list-item>
</v-col> </v-col>
</v-row> </v-row>
</div>
</template> </template>

View File

@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import { BandModel } from '@/data/models/acts/bandModel';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import ratingSection from './ratingSection.vue'; import ratingSection from './ratingSection.vue';
import bandMemberSection from './bandMemberSection.vue'; import bandMemberSection from './bandMemberSection.vue';
@@ -11,11 +10,13 @@ import { useShoppingStore } from '@/data/stores/shoppingStore';
import { ref } from 'vue'; import { ref } from 'vue';
import { useFeedbackStore } from '@/data/stores/feedbackStore'; import { useFeedbackStore } from '@/data/stores/feedbackStore';
import { getBand } from '@/data/api/bandApi'; import { getBand } from '@/data/api/bandApi';
import { BandApiModel } from '@/data/models/acts/bandApiModel';
import { genre } from 'backend/routes/genre.routes';
const router = useRouter() const router = useRouter()
const shoppingStore = useShoppingStore() const shoppingStore = useShoppingStore()
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
const band = ref<BandModel>(new BandModel()) const band = ref<BandApiModel>(new BandApiModel())
feedbackStore.fetchDataFromServerInProgress = true feedbackStore.fetchDataFromServerInProgress = true
@@ -47,7 +48,10 @@ getBand(String(router.currentRoute.value.params.bandName).replaceAll('-', ' '))
</v-col> </v-col>
</v-row> </v-row>
<concert-section :band="band"/> <concert-section
:band="band"
:events="band.events"
/>
<v-row> <v-row>
<v-col> <v-col>
@@ -64,7 +68,7 @@ getBand(String(router.currentRoute.value.params.bandName).replaceAll('-', ' '))
</v-col> </v-col>
</v-row> </v-row>
<rating-section :band="band" /> <rating-section :ratings="band.ratings" />
<v-row> <v-row>

View File

@@ -1,13 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { BandModel } from '@/data/models/acts/bandModel'; import { BandApiModel } from '@/data/models/acts/bandApiModel';
import { useFeedbackStore } from '@/data/stores/feedbackStore'; import { RatingModel } from '@/data/models/acts/ratingModel';
import { calcRating, calcRatingValues } from '@/scripts/concertScripts'; import { calcRating, calcRatingValues } from '@/scripts/concertScripts';
const feedbackStore = useFeedbackStore()
defineProps({ defineProps({
band: { ratings: {
type: BandModel, type: Array<RatingModel>,
required: true required: true
} }
}) })
@@ -18,32 +16,32 @@ defineProps({
<v-col> <v-col>
<div class="d-flex align-center justify-center flex-column" style="height: 100%;"> <div class="d-flex align-center justify-center flex-column" style="height: 100%;">
<div class="text-h2 mt-5"> <div class="text-h2 mt-5">
{{ calcRating(band.ratings).toFixed(1) }} {{ calcRating(ratings).toFixed(1) }}
<span class="text-h6 ml-n3">/5</span> <span class="text-h6 ml-n3">/5</span>
</div> </div>
<v-rating <v-rating
:model-value="calcRating(band.ratings)" :model-value="calcRating(ratings)"
color="yellow-darken-3" color="yellow-darken-3"
half-increments half-increments
size="x-large" size="x-large"
readonly readonly
/> />
<div class="px-3 text-h6">{{ band.ratings.length }} {{ $t('rating', band.ratings.length) }}</div> <div class="px-3 text-h6">{{ ratings.length }} {{ $t('rating', ratings.length) }}</div>
</div> </div>
</v-col> </v-col>
<v-col> <v-col>
<v-list> <v-list>
<v-list-item v-for="ratingValue in calcRatingValues(band.ratings)"> <v-list-item v-for="ratingValue in calcRatingValues(ratings)">
<template v-slot:prepend> <template v-slot:prepend>
<span>{{ ratingValue.value }}</span> <span>{{ ratingValue.value }}</span>
<v-icon class="ml-3 mr-n3" icon="mdi-star" /> <v-icon class="ml-3 mr-n3" icon="mdi-star" />
</template> </template>
<v-progress-linear <v-progress-linear
:model-value="(ratingValue.count / band.ratings.length) * 100" :model-value="(ratingValue.count / ratings.length) * 100"
height="20" height="20"
color="yellow-darken-3" color="yellow-darken-3"
rounded rounded

View File

@@ -33,9 +33,10 @@ shoppingStore.getEvents()
v-for="i in 3" v-for="i in 3"
> >
<v-col> <v-col>
<event-list-item Loading...
<!-- todo <event-list-item
:loading="true" :loading="true"
/> /> -->
</v-col> </v-col>
</v-row> </v-row>
@@ -46,6 +47,8 @@ shoppingStore.getEvents()
<v-col> <v-col>
<event-list-item <event-list-item
:event="event" :event="event"
:band="event.band"
:concerts="event.concerts"
/> />
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -8,12 +8,12 @@ import { useRouter } from 'vue-router';
import sectionDivider from '@/components/basics/sectionDivider.vue'; import sectionDivider from '@/components/basics/sectionDivider.vue';
import { useBasketStore } from '@/data/stores/basketStore'; import { useBasketStore } from '@/data/stores/basketStore';
import concertListItem from '@/components/pageParts/concertListItem.vue'; import concertListItem from '@/components/pageParts/concertListItem.vue';
import { ConcertModel } from '@/data/models/acts/concertModel';
import outlinedButton from '@/components/basics/outlinedButton.vue'; import outlinedButton from '@/components/basics/outlinedButton.vue';
import { ConcertApiModel } from '@/data/models/acts/concertApiModel';
const router = useRouter() const router = useRouter()
const seatGroups = ref<Array<SeatGroupModel>>() const seatGroups = ref<Array<SeatGroupModel>>()
const concertModel = ref<ConcertModel>(new ConcertModel()) const concertModel = ref<ConcertApiModel>(new ConcertApiModel())
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
const basketStore = useBasketStore() const basketStore = useBasketStore()
@@ -42,12 +42,10 @@ getConcert(Number(router.currentRoute.value.params.id))
<v-row> <v-row>
<v-col> <v-col>
<concert-list-item <concert-list-item
:concert="concertModel"
:loading="feedbackStore.fetchDataFromServerInProgress" :loading="feedbackStore.fetchDataFromServerInProgress"
:link="false" :link="false"
:title="concertModel.location.city.name" :title="concertModel.location.city.name"
:image="concertModel.location.imageOutdoor"
:date="concertModel.date"
:price="concertModel.price"
:show-button="false" :show-button="false"
> >
<template #description> <template #description>
@@ -79,7 +77,10 @@ getConcert(Number(router.currentRoute.value.params.id))
<v-col v-else> <v-col v-else>
<seat-plan-map <seat-plan-map
:concert="concertModel" :seat-groups="seatGroups" :location="concertModel.location" /> :concert="concertModel"
:seat-groups="seatGroups"
:location="concertModel.location"
/>
</v-col> </v-col>
</v-row> </v-row>
@@ -103,7 +104,8 @@ getConcert(Number(router.currentRoute.value.params.id))
<v-row class="pb-5"> <v-row class="pb-5">
<outlined-button <outlined-button
prepend-icon="mdi-basket-plus" prepend-icon="mdi-basket-plus"
@click="basketStore.moveSeatSelectionsToBasket(); router.push('/basket')" @click="basketStore.moveSeatSelectionsToBasket(concertModel.event, concertModel.event.band);
router.push('/basket')"
:disabled="basketStore.selectedSeats.length == 0" :disabled="basketStore.selectedSeats.length == 0"
block block
> >

View File

@@ -9,13 +9,14 @@ import { useFeedbackStore } from '@/data/stores/feedbackStore';
import { ref } from 'vue'; import { ref } from 'vue';
import { EventModel } from '@/data/models/acts/eventModel'; import { EventModel } from '@/data/models/acts/eventModel';
import { getTopEvents } from '@/data/api/eventApi'; import { getTopEvents } from '@/data/api/eventApi';
import { LocationModel } from '@/data/models/locations/locationModel';
import { getTopLocations } from '@/data/api/locationApi'; import { getTopLocations } from '@/data/api/locationApi';
import { LocationApiModel } from '@/data/models/locations/locationApiModel';
import { EventApiModel } from '@/data/models/acts/eventApiModel';
const router = useRouter() const router = useRouter()
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
const topEvents = ref<Array<EventModel>>(Array.from({length: 4}, () => new EventModel())) const topEvents = ref<Array<EventApiModel>>(Array.from({length: 4}, () => new EventApiModel()))
const topLocations = ref<Array<LocationModel>>(Array.from({length: 8}, () => new LocationModel())) const topLocations = ref<Array<LocationApiModel>>(Array.from({length: 8}, () => new LocationApiModel()))
feedbackStore.fetchDataFromServerInProgress = true feedbackStore.fetchDataFromServerInProgress = true
@@ -54,7 +55,7 @@ getTopEvents(4)
@click="router.push('/bands/' + topEvents[i - 1].band.name.replaceAll(' ', '-').toLowerCase())" @click="router.push('/bands/' + topEvents[i - 1].band.name.replaceAll(' ', '-').toLowerCase())"
:loading="feedbackStore.fetchDataFromServerInProgress" :loading="feedbackStore.fetchDataFromServerInProgress"
> >
ab {{ lowestTicketPrice(topEvents[i - 1]) }} ab {{ lowestTicketPrice(topEvents[i - 1].concerts) }}
</card-with-top-image> </card-with-top-image>
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -1,20 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { LocationModel } from '@/data/models/locations/locationModel';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import sectionDivider from '@/components/basics/sectionDivider.vue'; import sectionDivider from '@/components/basics/sectionDivider.vue';
import { dateStringToHumanReadableString } from '@/scripts/dateTimeScripts';
import seatPlanMap from '@/components/seatPlanMap/seatPlanMap.vue'; import seatPlanMap from '@/components/seatPlanMap/seatPlanMap.vue';
import { useShoppingStore } from '@/data/stores/shoppingStore';
import { getLocation } from '@/data/api/locationApi'; import { getLocation } from '@/data/api/locationApi';
import { ref } from 'vue'; import { ref } from 'vue';
import { useFeedbackStore } from '@/data/stores/feedbackStore'; import { useFeedbackStore } from '@/data/stores/feedbackStore';
import heroImage from '@/components/pageParts/heroImage.vue'; import heroImage from '@/components/pageParts/heroImage.vue';
import concertListItem from '@/components/pageParts/concertListItem.vue'; import concertListItem from '@/components/pageParts/concertListItem.vue';
import { LocationApiModel } from '@/data/models/locations/locationApiModel';
const router = useRouter() const router = useRouter()
const shoppingStore = useShoppingStore()
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
const location = ref<LocationModel>(new LocationModel()) const location = ref<LocationApiModel>(new LocationApiModel())
feedbackStore.fetchDataFromServerInProgress = true feedbackStore.fetchDataFromServerInProgress = true
@@ -22,7 +19,6 @@ getLocation(String(router.currentRoute.value.params.locationName))
.then(result => { .then(result => {
location.value = result.data location.value = result.data
feedbackStore.fetchDataFromServerInProgress = false feedbackStore.fetchDataFromServerInProgress = false
console.log(location.value.seatGroups)
}) })
</script> </script>
@@ -53,7 +49,8 @@ getLocation(String(router.currentRoute.value.params.locationName))
<v-row v-if="feedbackStore.fetchDataFromServerInProgress" v-for="i in 3"> <v-row v-if="feedbackStore.fetchDataFromServerInProgress" v-for="i in 3">
<v-col class="text-center"> <v-col class="text-center">
<concert-list-item :loading="feedbackStore.fetchDataFromServerInProgress" /> Loading...
<!-- todo <concert-list-item :loading="feedbackStore.fetchDataFromServerInProgress" /> -->
</v-col> </v-col>
</v-row> </v-row>
@@ -63,14 +60,12 @@ getLocation(String(router.currentRoute.value.params.locationName))
> >
<v-col> <v-col>
<concert-list-item <concert-list-item
:date="concert.date" :concert="concert"
:title="concert.event.name" :title="concert.event.name"
:in-stock="concert.inStock" :onClick="() => router.push('/bands/' + concert.event.band.name.replaceAll(' ', '-').toLowerCase())"
:price="concert.price"
:onClick="() => router.push('/bands/' + concert.event.bandName.replaceAll(' ', '-').toLowerCase())"
> >
<template #description> <template #description>
{{ concert.event.bandName }} {{ concert.event.band.name }}
</template> </template>
</concert-list-item> </concert-list-item>
</v-col> </v-col>

View File

@@ -1,14 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import sectionDivider from '@/components/basics/sectionDivider.vue'; import sectionDivider from '@/components/basics/sectionDivider.vue';
import cardWithTopImage from '@/components/basics/cardViewTopImage.vue'; import cardWithTopImage from '@/components/basics/cardViewTopImage.vue';
import { useRouter } from 'vue-router';
import { useShoppingStore } from '@/data/stores/shoppingStore'; import { useShoppingStore } from '@/data/stores/shoppingStore';
import { useFeedbackStore } from '@/data/stores/feedbackStore'; import { useFeedbackStore } from '@/data/stores/feedbackStore';
import locationListItem from '@/components/pageParts/locationListItem.vue'; import locationListItem from '@/components/pageParts/locationListItem.vue';
const shoppingStore = useShoppingStore() const shoppingStore = useShoppingStore()
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
const router = useRouter()
shoppingStore.getCities() shoppingStore.getCities()
</script> </script>
@@ -52,7 +50,10 @@ shoppingStore.getCities()
<v-row> <v-row>
<v-col v-for="location in city.locations" cols="3"> <v-col v-for="location in city.locations" cols="3">
<location-list-item :location="location" /> <location-list-item
:location="location"
:concerts="location.concerts"
/>
</v-col> </v-col>
</v-row> </v-row>
</div> </div>

View File

@@ -46,7 +46,12 @@ const searchStore = useSearchStore()
v-for="event in searchStore.events" v-for="event in searchStore.events"
> >
<v-col> <v-col>
<event-list-item :event="event" :loading="searchStore.searchInProgress" /> <event-list-item
:event="event"
:band="event.band"
:concerts="event.concerts"
:loading="searchStore.searchInProgress"
/>
</v-col> </v-col>
</v-row> </v-row>
@@ -55,7 +60,10 @@ const searchStore = useSearchStore()
v-for="i in 3" v-for="i in 3"
> >
<v-col> <v-col>
<event-list-item :loading="searchStore.searchInProgress" /> Loading...
<!-- <event-list-item
:loading="searchStore.searchInProgress"
/> -->
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -1,7 +1,6 @@
import { RatingModel } from "@/data/models/acts/ratingModel" import { RatingModel } from "@/data/models/acts/ratingModel"
import { dateToHumanReadableString } from "./dateTimeScripts" import { dateToHumanReadableString } from "./dateTimeScripts"
import { TourModel } from "@/data/models/acts/tourModel" import { ConcertModel } from "@/data/models/acts/concertModel"
import { EventModel } from "@/data/models/acts/eventModel"
/** /**
* Calculate a price based on parameters * Calculate a price based on parameters
@@ -16,6 +15,7 @@ export function calcPrice(price: number, quantity: number = 1): number {
return Math.round(quantity * price * 100) / 100 return Math.round(quantity * price * 100) / 100
} }
/** /**
* Calculate the average of an Array of ratings * Calculate the average of an Array of ratings
* *
@@ -33,6 +33,14 @@ export function calcRating(ratings: Array<RatingModel>) {
return sum / ratings.length return sum / ratings.length
} }
/**
* Classifies a bunch of ratings to groups from 1 to 5 stars
*
* @param ratings Array of RatingModels
*
* @returns Array of Objects: { value: number[1-5], count: number }
*/
export function calcRatingValues(ratings: Array<RatingModel>) { export function calcRatingValues(ratings: Array<RatingModel>) {
let ratingValues = [ let ratingValues = [
{ value: 1, count: 0 }, { value: 1, count: 0 },
@@ -57,10 +65,10 @@ export function calcRatingValues(ratings: Array<RatingModel>) {
* *
* @returns A date string. If one concert: dd.MM.YYYY, if two or more: dd.MM.YYYY - dd.MM.YYYY * @returns A date string. If one concert: dd.MM.YYYY, if two or more: dd.MM.YYYY - dd.MM.YYYY
*/ */
export function createDateRangeString(event: EventModel) { export function createDateRangeString(concerts: Array<ConcertModel>) {
const dateArray: Array<Date> = [] const dateArray: Array<Date> = []
for (let concert of event.concerts) { for (let concert of concerts) {
dateArray.push(new Date(concert.date)) dateArray.push(new Date(concert.date))
} }
@@ -85,10 +93,10 @@ export function createDateRangeString(event: EventModel) {
* *
* @returns Lowest ticket price, rounded to two floating point digits * @returns Lowest ticket price, rounded to two floating point digits
*/ */
export function lowestTicketPrice(event: EventModel): string { export function lowestTicketPrice(concerts: Array<ConcertModel>): string {
const priceArray : Array<number> = [] const priceArray : Array<number> = []
for (let concert of event.concerts) { for (let concert of concerts) {
priceArray.push(concert.price) priceArray.push(concert.price)
} }

View File

@@ -1,9 +1,23 @@
/**
* Concert a date object to german time string
*
* @param date Date object
*
* @returns German date string, e.g. 31.12.2024
*/
export function dateToHumanReadableString(date: Date) { export function dateToHumanReadableString(date: Date) {
return String(date.getDate()).padStart(2, '0') + '.' + return String(date.getDate()).padStart(2, '0') + '.' +
String(date.getMonth() + 1).padStart(2, '0') + '.' + String(date.getMonth() + 1).padStart(2, '0') + '.' +
date.getFullYear() date.getFullYear()
} }
/**
* Convert ISO time string to german time string
*
* @param string ISO time string, e.g. 2024-12-31
*
* @returns German date string, e.g. 31.12.2024
*/
export function dateStringToHumanReadableString(string: string) { export function dateStringToHumanReadableString(string: string) {
return dateToHumanReadableString(new Date(string)) return dateToHumanReadableString(new Date(string))
} }