Display concerts with card views on "All concerts" page, adding image property for tours

This commit is contained in:
2024-09-28 21:18:25 +02:00
parent 9b325c849e
commit 4bcc2b86d5
49 changed files with 454 additions and 219 deletions

View File

@@ -239,6 +239,60 @@
"bandId": 4
}
]
},
{
"id": 5,
"name": "Billy Talent",
"foundingYear": 1993,
"descriptionEn": "Billy Talent is a Canadian rock band from Mississauga, Ontario. They formed in 1993 with lead vocalist Benjamin Kowalewicz, guitarist Ian D'Sa, bassist Jonathan Gallant, and drummer Aaron Solowoniuk. There have been no lineup changes, although Solowoniuk has been on hiatus from the band since 2016 due to a relapse of multiple sclerosis. In the three decades since their inception, Billy Talent has sold well over a million physical albums in Canada alone and nearly 3 million albums internationally. During their most successful period, they were ranked as one of the top 10 best-selling native bands in Canada.",
"descriptionDe": "Billy Talent ist eine kanadische Rockband aus Mississauga, Ontario. Die Band spielte anfangs Punk, ordnet sich auf den späteren Alben jedoch eher im Alternative Rock ein. ",
"images": [ "billy-talent-1.jpg" ],
"logo": "billy-talent-logo.png",
"genreId": [ 2, 11, 12, 13 ],
"members": [
{
"name": "Benjamin Kowalewicz",
"bandId": 5,
"image": "benjamin-kowalewicz.jpg"
},
{
"name": "Ian D'Sa ",
"bandId": 5,
"image": "ian-d-sa.jpg"
},
{
"name": "Jonathan Gallant",
"bandId": 5,
"image": "jonathan-gallant.jpg"
},
{
"name": "Jordan Hastings",
"bandId": 5,
"image": "jordan-hastings.jpg"
},
{
"name": "Josh Freese",
"bandId": 5,
"image": "josh-freese.jpg"
}
],
"ratings": [
{
"accountId": 0,
"rating": 5,
"bandId": 5
},
{
"accountId": 1,
"rating": 3,
"bandId": 5
},
{
"accountId": 2,
"rating": 4,
"bandId": 5
}
]
}
]
}

View File

@@ -1,39 +0,0 @@
{
"data": [
{
"id": 0,
"icon": "",
"name": "All"
},
{
"id": 1,
"icon": "mdi-chip",
"name": "Electronic"
},
{
"id": 2,
"icon": "mdi-soccer",
"name": "Sports"
},
{
"id": 3,
"icon": "mdi-tshirt-crew",
"name": "Clothes"
},
{
"id": 4,
"icon": "mdi-bookshelf",
"name": "Books"
},
{
"id": 5,
"icon": "mdi-guitar-electric",
"name": "Instruments"
},
{
"id": 6,
"icon": "mdi-teddy-bear",
"name": "Toys"
}
]
}

View File

@@ -43,6 +43,18 @@
{
"id": 10,
"name": "Post-Grunge"
},
{
"id": 11,
"name": "Punk-Rock"
},
{
"id": 12,
"name": "Post-Hardcore"
},
{
"id": 13,
"name": "Pop-Punk"
}
]
}

View File

@@ -2,40 +2,74 @@
"data": [
{
"id": 0,
"name": "Unlimited Love Tour",
"name": "Unlimited Love",
"bandId": 0,
"offered": true,
"shows": [
"image": "unlimited-love-tour.jpg",
"concerts": [
{
"id": 0,
"date": "2024-12-03",
"date": "2024-10-18",
"price": 92,
"inStock": 230,
"inStock": 930,
"locationId": 0,
"tourId": 0
},
{
"id": 1,
"date": "2024-10-23",
"price": 119.90,
"inStock": 8736,
"locationId": 4,
"tourId": 0
},
{
"id": 2,
"date": "2024-10-26",
"price": 114.90,
"inStock": 2793,
"locationId": 8,
"tourId": 0
},
{
"id": 3,
"date": "2024-11-02",
"price": 124.90,
"inStock": 3079,
"locationId": 12,
"tourId": 0
}
]
},
{
"id": 1,
"name": "The Bends Tour",
"name": "The Bends",
"bandId": 1,
"offered": true,
"shows": [
"image": "the-bends-tour.jpg",
"concerts": [
{
"id": 1,
"id": 4,
"date": "2024-11-30",
"price": 104,
"inStock": 120,
"locationId": 2,
"price": 108,
"inStock": 1200,
"locationId": 1,
"tourId": 1
},
{
"id": 2,
"id": 5,
"date": "2024-12-01",
"price": 104,
"inStock": 180,
"locationId": 0,
"inStock": 1800,
"locationId": 5,
"tourId": 1
},
{
"id": 6,
"date": "2024-12-07",
"price": 99.90,
"inStock": 2438,
"locationId": 9,
"tourId": 1
}
]
@@ -45,29 +79,55 @@
"name": "European Tour",
"bandId": 2,
"offered": true,
"shows": [
"image": "european-tour-arctic-monkeys.jpg",
"concerts": [
{
"id": 3,
"date": "2024-10-15",
"price": 80,
"inStock": 99,
"id": 7,
"date": "2025-01-21",
"price": 67.90,
"inStock": 994,
"locationId": 3,
"tourId": 2
},
{
"id": 8,
"date": "2024-11-15",
"price": 79.90,
"inStock": 1073,
"locationId": 14,
"tourId": 2
}
]
},
{
"id": 3,
"name": "Moon Music Tour",
"name": "Music of the Spheres",
"bandId": 3,
"offered": true,
"shows": [
"image": "music-of-the-spheres.png",
"concerts": [
{
"id": 4,
"date": "2024-10-15",
"price": 80,
"inStock": 99,
"locationId": 1,
"id": 9,
"date": "2024-12-07",
"price": 124.90,
"inStock": 765,
"locationId": 13,
"tourId": 3
},
{
"id": 10,
"date": "2025-01-17",
"price": 129.90,
"inStock": 989,
"locationId": 9,
"tourId": 3
},
{
"id": 11,
"date": "2025-02-01",
"price": 134.90,
"inStock": 827,
"locationId": 4,
"tourId": 3
}
]
@@ -77,16 +137,42 @@
"name": "But Here We Are Tour",
"bandId": 4,
"offered": true,
"shows": [
"image": "but-here-we-are.jpg",
"concerts": [
{
"id": 5,
"date": "2024-10-15",
"id": 12,
"date": "2024-12-05",
"price": 80,
"inStock": 99,
"locationId": 2,
"tourId": 4
}
]
},
{
"id": 5,
"name": "Crisis of Faith",
"bandId": 5,
"offered": true,
"image": "crisis-of-faith-tour.jpg",
"concerts": [
{
"id": 13,
"date": "2025-01-12",
"price": 81.90,
"inStock": 173,
"locationId": 2,
"tourId": 5
},
{
"id": 14,
"date": "2025-02-01",
"price": 84.90,
"inStock": 192,
"locationId": 6,
"tourId": 5
}
]
}
]
}

View File

@@ -11,7 +11,7 @@ import { AccountRole } from "./models/user/accountRole.model"
import { Genre } from "./models/acts/genre.model"
import { Location } from "./models/acts/location.model"
import { Band } from "./models/acts/band.model"
import { Show } from "./models/acts/show.model"
import { Concert } from "./models/acts/concert.model"
import { Member } from "./models/acts/member.model"
import { Rating } from "./models/acts/rating.model"
import { Tour } from "./models/acts/tour.model"
@@ -31,7 +31,7 @@ export const sequelize = new Sequelize({
storage: "database.sqlite",
models: [
AccountRole, Account, Payment, Address,
City, Location, Genre, Band, BandGenre, Rating, Member, Tour, Show,
City, Location, Genre, Band, BandGenre, Rating, Member, Tour, Concert,
Order, OrderItem
]
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -4,7 +4,7 @@ import { Tour } from "./tour.model";
import { OrderItem } from "../ordering/orderItem.model";
@Table({ timestamps: false })
export class Show extends Model {
export class Concert extends Model {
@Column
date: String

View File

@@ -1,5 +1,5 @@
import { BelongsTo, Column, ForeignKey, HasMany, Model, Table } from "sequelize-typescript";
import { Show } from "./show.model";
import { Concert } from "./concert.model";
import { City } from "./city.model";
@Table({ timestamps: false })
@@ -20,8 +20,8 @@ export class Location extends Model {
// Relations
@HasMany(() => Show)
shows: Show[]
@HasMany(() => Concert)
shows: Concert[]
@BelongsTo(() => City)
city: City

View File

@@ -1,6 +1,6 @@
import { BelongsTo, Column, ForeignKey, HasMany, Model, Table } from "sequelize-typescript";
import { Band } from "./band.model";
import { Show } from "./show.model";
import { Concert } from "./concert.model";
@Table({ timestamps: false })
export class Tour extends Model {
@@ -13,12 +13,15 @@ export class Tour extends Model {
@Column
offered: Boolean
@Column
image: String
// Relations
@BelongsTo(() => Band)
band: Band
@HasMany(() => Show)
shows: Show[]
@HasMany(() => Concert)
shows: Concert[]
}

View File

@@ -1,5 +1,5 @@
import { Model, BelongsTo, Column, ForeignKey, HasMany, HasOne, Table } from "sequelize-typescript";
import { Show } from "../acts/show.model";
import { Concert } from "../acts/concert.model";
import { Order } from "./order.model";
@Table({ timestamps: false })
@@ -15,7 +15,7 @@ export class OrderItem extends Model {
orderPrice: number
@Column
@ForeignKey(() => Show)
@ForeignKey(() => Concert)
showId: number
@@ -23,6 +23,6 @@ export class OrderItem extends Model {
@BelongsTo(() => Order)
order: Order
@BelongsTo(() => Show)
product: Show
@BelongsTo(() => Concert)
product: Concert
}

View File

@@ -1,13 +1,13 @@
import { Location } from "../models/acts/location.model";
import { Show } from "../models/acts/show.model";
import { Concert } from "../models/acts/concert.model";
import { Request, Response, Router } from "express";
import { Tour } from "../models/acts/tour.model";
import { City } from "../models/acts/city.model";
export const show = Router()
export const concert = Router()
show.get("/:id", (req: Request, res: Response) => {
Show.findByPk(req.params.id, {
concert.get("/:id", (req: Request, res: Response) => {
Concert.findByPk(req.params.id, {
include: [
Tour,
{

View File

@@ -1,6 +1,6 @@
import { Router, Request, Response } from "express";
import { Order } from "../models/ordering/order.model";
import { Show } from "../models/acts/show.model";
import { Concert } from "../models/acts/concert.model";
import { OrderItem } from "../models/ordering/orderItem.model";
import { Payment } from "../models/user/payment.model";
import { Address } from "../models/user/address.model";
@@ -18,7 +18,7 @@ order.get("/:id", (req: Request, res: Response) => {
model: OrderItem,
include: [
{
model: Show,
model: Concert,
include: [ Band, Location ],
attributes: {
exclude: [
@@ -50,7 +50,7 @@ order.post("/", (req: Request, res: Response) => {
productId: orderItem.productId
})
Show.decrement(
Concert.decrement(
"inStock",
{
by: orderItem.quantity,

View File

@@ -1,4 +1,4 @@
import { Show } from "../models/acts/show.model";
import { Concert } from "../models/acts/concert.model";
import { Band } from "../models/acts/band.model";
import { Tour } from "../models/acts/tour.model";
import { Request, Response, Router } from "express";
@@ -19,7 +19,7 @@ tour.get("/", (req: Request, res: Response) => {
}
},
{
model: Show,
model: Concert,
include: [
{
model: Location,

View File

@@ -9,7 +9,7 @@ import { Member } from '../models/acts/member.model'
import { Genre } from '../models/acts/genre.model'
import { Band } from '../models/acts/band.model'
import { Location } from '../models/acts/location.model'
import { Show } from '../models/acts/show.model'
import { Concert } from '../models/acts/concert.model'
import { Tour } from '../models/acts/tour.model'
import { City } from '../models/acts/city.model'
import { BandGenre } from '../models/acts/bandGenre.model'
@@ -36,7 +36,7 @@ export function deleteAllTables() {
Genre.destroy({ truncate: true })
Band.destroy({ truncate: true })
Location.destroy({ truncate: true })
Show.destroy({ truncate: true })
Concert.destroy({ truncate: true })
Address.destroy({ truncate: true })
Payment.destroy({ truncate: true })
@@ -90,8 +90,8 @@ export async function prepopulateDatabase() {
for (let tour of tours.data) {
await Tour.create(tour)
.then(async dataset => {
for (let show of tour.shows) {
await Show.create(show)
for (let concert of tour.concerts) {
await Concert.create(concert)
}
})
}

View File

@@ -5,7 +5,7 @@ import { api } from './routes/api.routes'
import { startDatabase } from './database'
import { order } from './routes/order.routes'
import { account } from './routes/account.routes'
import { show } from './routes/show.routes'
import { concert } from './routes/concert.routes'
import { band } from './routes/band.routes'
import { genre } from './routes/genre.routes'
import { location } from './routes/location.routes'
@@ -35,7 +35,7 @@ app.use('/static', express.static(path.join(__dirname, 'images')))
// Routes
app.use("/api", api)
app.use("/shows", show)
app.use("/shows", concert)
app.use("/bands", band)
app.use("/genres", genre)
app.use("/locations", location)

View File

@@ -6,17 +6,17 @@ import navigationAppendItems from './components/navigation/navigationAppendItems
import navigationPrependItems from './components/navigation/navigationPrependItems.vue';
import { usePreferencesStore } from './data/stores/preferencesStore';
import { useFeedbackStore } from './data/stores/feedbackStore';
import { useShowStore } from './data/stores/showStore';
import { useConcertStore } from './data/stores/concertStore';
const preferencesStore = usePreferencesStore()
const showStore = useShowStore()
const concertStore = useConcertStore()
const feedbackStore = useFeedbackStore()
const theme = useTheme()
theme.global.name.value = preferencesStore.theme
showStore.fetchAllTours()
concertStore.fetchAllTours()
// Global watcher
watch(() => preferencesStore.language, () => {
@@ -64,7 +64,7 @@ watch(() => preferencesStore.language, () => {
</v-snackbar>
<!-- Here changes the router the content -->
<v-container max-width="1200" class="pt-0 pb-5">
<v-container max-width="1400" class="pt-0 pb-5">
<v-sheet color="sheet">
<router-view></router-view>
</v-sheet>

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
defineProps({
image: String,
title: String
})
</script>
<template>
<v-card
variant="tonal"
link
>
<v-row>
<v-col cols="auto" class="pr-0">
<v-img :src="image" aspect-ratio="1" width="140" cover />
</v-col>
<v-col class="pl-0">
<v-card-title v-if="title">
{{ title }}
</v-card-title>
<div class="px-4 pb-4" v-if="$slots.default">
<slot></slot>
</div>
</v-col>
<v-col cols="2" height="100%">
<slot name="append"></slot>
</v-col>
</v-row>
</v-card>
</template>

View File

@@ -1,7 +1,11 @@
<script setup lang="ts">
defineProps({
image: String,
title: String
title: String,
smallerTitle: {
type: Boolean,
default: false
}
})
</script>
@@ -17,10 +21,17 @@ defineProps({
cover
/>
<v-card-title v-if="title">
<div v-if="title">
<v-card-title v-if="!smallerTitle">
{{ title }}
</v-card-title>
<v-card-title v-else style="font-size: medium">
{{ title }}
</v-card-title>
</div>
<div class="px-4 pb-4" v-if="$slots.default">
<slot></slot>
</div>

View File

@@ -10,12 +10,12 @@
<v-divider vertical />
<v-btn
to="/shows/events"
to="/shows/concerts"
prepend-icon="mdi-ticket"
height="100%"
:rounded="false"
>
{{ $t('menu.allEvents', 2) }}
{{ $t('menu.allConcerts', 2) }}
</v-btn>
<v-divider vertical />

View File

@@ -0,0 +1,7 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/concerts"
export async function getAllConcerts() {
return await axios.get(BASE_URL)
}

View File

@@ -1,7 +0,0 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/shows"
export async function getAllShows() {
return await axios.get(BASE_URL)
}

View File

@@ -12,7 +12,7 @@ export class BandModel {
logo: string
ratings: Array<RatingModel>
members: Array<MemberModel>
genre: {
genres: {
name: string
}
}

View File

@@ -1,12 +1,12 @@
import { ShowModel } from "./showModel"
import { ConcertModel } from "./concertModel"
export class BasketItemModel {
id: number = -1
quantity: number = 1
product: ShowModel = new ShowModel()
concert: ConcertModel = new ConcertModel()
constructor(quantity: number, product: ShowModel) {
constructor(quantity: number, concert: ConcertModel) {
this.quantity = quantity
this.product = product
this.concert = concert
}
}

View File

@@ -1,6 +1,6 @@
import { LocationModel } from "./locationModel"
export class ShowModel {
export class ConcertModel {
id: number
inStock: number
date: string

View File

@@ -1,8 +1,8 @@
import { ShowModel } from "./showModel"
import { ConcertModel } from "./concertModel"
export class OrderItemModel {
orderId: number = -1
quantity: number = 1
orderPrice: number = 0
product: ShowModel
product: ConcertModel
}

View File

@@ -1,4 +1,3 @@
import { AccountModel } from "./accountModel"
import { BandModel } from "./bandModel"
export class RatingModel {

View File

@@ -1,10 +1,11 @@
import { BandModel } from "./bandModel"
import { ShowModel } from "./showModel"
import { ConcertModel } from "./concertModel"
export class TourModel {
id: number
name: string
offered: boolean
band: BandModel
shows: Array<ShowModel>
image: string
shows: Array<ConcertModel>
}

View File

@@ -5,7 +5,7 @@ import { useFeedbackStore } from "./feedbackStore";
import { BannerStateEnum } from "../enums/bannerStateEnum";
import { addOrder } from "../api/orderApi";
import { useAccountStore } from "./accountStore";
import { ShowModel } from "../models/showModel";
import { ConcertModel } from "../models/concertModel";
import { AddressModel } from "../models/addressModel";
import { PaymentModel } from "../models/paymentModel";
@@ -44,28 +44,28 @@ export const useBasketStore = defineStore('basketStore', {
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTREMOVED)
this.itemsInBasket = this.itemsInBasket.filter((basketItemModel: BasketItemModel) =>
basketItemModel.product.id != item.product.id
basketItemModel.concert.id != item.concert.id
)
},
/**
* Add an item to the basket. If the product is already in the basket, the quantity will increase
*
* @param show Show to add
* @param concert Concert to add
* @param quantity Quantity of the product
*/
addItemToBasket(show: ShowModel, quantity: number) {
addItemToBasket(concert: ConcertModel, quantity: number) {
const feedbackStore = useFeedbackStore()
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTADDED)
// Product is already in the basket, increase number of items
if (this.itemsInBasket.find((basketItem: BasketItemModel) =>
basketItem.product.id == show.id))
basketItem.concert.id == concert.id))
{
this.itemsInBasket.find((basketItem: BasketItemModel) =>
basketItem.product.id == show.id).quantity += quantity
basketItem.concert.id == concert.id).quantity += quantity
} else {
this.itemsInBasket.push(new BasketItemModel(quantity, show))
this.itemsInBasket.push(new BasketItemModel(quantity, concert))
}
},

View File

@@ -11,13 +11,13 @@ import { getAllGenres } from "../api/genreApi";
import { CityModel } from "../models/cityModel";
import { getAllCities } from "../api/cityApi";
export const useShowStore = defineStore("showStore", {
export const useConcertStore = defineStore("concertStore", {
state: () => ({
tours: useLocalStorage<Array<TourModel>>("hackmycart/showStore/tours", []),
bands: useLocalStorage<Array<BandModel>>("hackmycart/showStore/bands", []),
locations: useLocalStorage<Array<LocationModel>>("hackmycart/showStore/locations", []),
cities: useLocalStorage<Array<CityModel>>("hackmycart/showStore/cities", []),
genres: useLocalStorage<Array<GenreModel>>("hackmycart/showStore/genres", [])
tours: useLocalStorage<Array<TourModel>>("hackmycart/concertStore/tours", []),
bands: useLocalStorage<Array<BandModel>>("hackmycart/concertStore/bands", []),
locations: useLocalStorage<Array<LocationModel>>("hackmycart/concertStore/locations", []),
cities: useLocalStorage<Array<CityModel>>("hackmycart/concertStore/cities", []),
genres: useLocalStorage<Array<GenreModel>>("hackmycart/concertStore/genres", [])
}),
actions: {
@@ -25,6 +25,10 @@ export const useShowStore = defineStore("showStore", {
await getAllTours()
.then(result => {
this.tours = result.data
this.tours.sort((a, b) => {
return new Date(a.shows[0].date) < new Date(b.shows[0].date) ? -1 : 1
})
})
await getAllBands()

View File

@@ -5,7 +5,7 @@ import { LanguageEnum } from "../enums/languageEnum";
export const usePreferencesStore = defineStore('preferencesStore', {
state: () => ({
theme: useLocalStorage<ThemeEnum>("hackmycart/preferencesStore/theme", ThemeEnum.DARKRED),
theme: useLocalStorage<ThemeEnum>("hackmycart/preferencesStore/theme", ThemeEnum.DARKBLUE),
language: useLocalStorage<LanguageEnum>("hackmycart/preferencesStore/language", LanguageEnum.GERMAN)
}),
})

View File

@@ -1,12 +1,13 @@
{
"menu": {
"allEvents": "Alle Events",
"allConcerts": "Alle Konzerte",
"allLocations": "Alle Veranstaltungsorte",
"allBands": "Alle Bands"
},
"shows": {
"highlights": "Highlights",
"tickets": "Ticket | Tickets",
"topEvents": "Top Events",
"topConcerts": "Top Konzerte",
"topBands": "Top Bands",
"topLocations": "Top Veranstaltungsorte"
},

View File

@@ -1,12 +1,13 @@
{
"menu": {
"allEvents": "All Events",
"allConcerts": "All Concerts",
"allLocations": "All Locations",
"allBands": "All Bands"
},
"shows": {
"highlights": "Highlights",
"tickets": "Ticket | Tickets",
"topEvents": "Top Events",
"topConcerts": "Top Concerts",
"topBands": "Top Bands",
"topLocations": "Top Locations"
},

View File

@@ -30,18 +30,18 @@ function editQuantity(basketItem: BasketItemModel) {
<tbody>
<tr v-for="basketItem in basketStore.itemsInBasket">
<!-- Category icon and name -->
<td><v-icon :icon="basketItem.product.category.icon" />
{{ basketItem.product.category.name }}
<td><v-icon :icon="basketItem.concert.category.icon" />
{{ basketItem.concert.category.name }}
</td>
<!-- Product brand -->
<td>
{{ basketItem.product.brand.name }}
{{ basketItem.concert.brand.name }}
</td>
<!-- Name of product -->
<td>
{{ basketItem.product.name }}
{{ basketItem.concert.name }}
</td>
<!-- Quantity -->
@@ -73,18 +73,18 @@ function editQuantity(basketItem: BasketItemModel) {
<!-- Total price -->
<td class="text-right">
<div v-if="basketItem.product.discount > 0">
<div v-if="basketItem.concert.discount > 0">
<strong class="font-weight-bold text-body-1 text-red-lighten-1">
{{ calcPrice(basketItem.product.price, basketItem.product.discount, basketItem.quantity) }}
{{ calcPrice(basketItem.concert.price, basketItem.concert.discount, basketItem.quantity) }}
</strong>
<div class="text-decoration-line-through ml-3 mt-1 text-caption">
{{ calcPrice(basketItem.product.price, 0, basketItem.quantity) }}
{{ calcPrice(basketItem.concert.price, 0, basketItem.quantity) }}
</div>
</div>
<div v-else>
{{ calcPrice(basketItem.product.price, 0, basketItem.quantity) }}
{{ calcPrice(basketItem.concert.price, 0, basketItem.quantity) }}
</div>
</td>

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
import OutlinedButton from '@/components/outlinedButton.vue';
import { useShowStore } from '@/data/stores/showStore';
import { useConcertStore } from '@/data/stores/concertStore';
const tourStore = useShowStore()
const concertStore = useConcertStore()
</script>
<template>
@@ -29,7 +29,7 @@ const tourStore = useShowStore()
</template>
<v-carousel-item
v-for="tour in tourStore.tours"
v-for="tour in concertStore.tours"
:src="'http://localhost:3000/static/bands/' + tour.band.images[0]"
cover
>

View File

@@ -1,13 +1,13 @@
<script setup lang="ts">
import { useShowStore } from '@/data/stores/showStore';
import { useConcertStore } from '@/data/stores/concertStore';
import highlightCarousel from './highlightCarousel.vue';
import sectionDivider from '@/components/sectionDivider.vue';
import cardWithTopImage from '@/components/cardWithTopImage.vue';
import { calcRating } from '@/scripts/showsScripts';
import { calcRating, lowestTicketPrice } from '@/scripts/concertScripts';
import OutlinedButton from '@/components/outlinedButton.vue';
import router from '@/plugins/router';
const showStore = useShowStore()
const concertStore = useConcertStore()
</script>
<template>
@@ -16,17 +16,18 @@ const showStore = useShowStore()
<v-container>
<v-row>
<v-col>
<section-divider :title="$t('shows.topEvents')" />
<section-divider :title="$t('shows.highlights')" />
</v-col>
</v-row>
<v-row>
<v-col v-for="i in 4" cols="3">
<v-col v-for="i in 6" cols="2">
<card-with-top-image
:image="'bands/' + showStore.tours[i].band.images[0]"
:title="showStore.tours[i].name"
:image="'tours/' + concertStore.tours[i - 1].image"
:title="concertStore.tours[i - 1].band.name"
smaller-title
>
{{ showStore.bands[i].name }}
Tickets ab {{ lowestTicketPrice(concertStore.tours[i - 1]) }}
</card-with-top-image>
</v-col>
</v-row>
@@ -35,10 +36,10 @@ const showStore = useShowStore()
<v-col>
<outlined-button
append-icon="mdi-chevron-right"
@click="router.push('/shows/events')"
@click="router.push('/shows/concerts')"
block
>
{{ $t('menu.allEvents') }}
{{ $t('menu.allConcerts') }}
</outlined-button>
</v-col>
</v-row>
@@ -53,10 +54,10 @@ const showStore = useShowStore()
<v-row>
<v-col v-for="i in 4" cols="3">
<card-with-top-image
:image="'bands/' + showStore.bands[i - 1].logo"
:title="showStore.bands[i - 1].name"
:image="'bands/' + concertStore.bands[i - 1].logo"
:title="concertStore.bands[i - 1].name"
>
{{ showStore.bands[i - 1].genre.name }}
{{ concertStore.bands[i - 1].genres.name }}
<div class="d-flex justify-center pt-3">
<v-rating
@@ -65,7 +66,7 @@ const showStore = useShowStore()
size="large"
half-increments
active-color="orange"
:model-value="calcRating(showStore.bands[i - 1].ratings)"
:model-value="calcRating(concertStore.bands[i - 1].ratings)"
/>
</div>
</card-with-top-image>
@@ -92,13 +93,13 @@ const showStore = useShowStore()
</v-row>
<v-row>
<v-col v-for="i in 4" cols="3">
<v-col v-for="i in 6" cols="2">
<card-with-top-image
:image="'locations/' + showStore.locations[i + 2].image"
:title="showStore.locations[i + 2].name"
:image="'locations/' + concertStore.locations[i + 2].image"
:title="concertStore.locations[i + 2].name"
smaller-title
>
<div>{{ showStore.locations[i + 2].address }}</div>
{{ showStore.locations[i + 2].city.name }}, {{ showStore.locations[i + 2].city.country }}
{{ concertStore.locations[i + 2].city.name }}, {{ concertStore.locations[i + 2].city.country }}
</card-with-top-image>
</v-col>
</v-row>
@@ -116,5 +117,3 @@ const showStore = useShowStore()
</v-row>
</v-container>
</template>

View File

@@ -1,15 +1,15 @@
<script setup lang="ts">
import sectionDivider from '@/components/sectionDivider.vue';
import { useShowStore } from '@/data/stores/showStore';
import { useConcertStore } from '@/data/stores/concertStore';
import cardWithTopImage from '@/components/cardWithTopImage.vue';
import { calcRating } from '@/scripts/showsScripts';
import { calcRating } from '@/scripts/concertScripts';
const showStore = useShowStore()
const concertStore = useConcertStore()
</script>
<template>
<v-container>
<div v-for="genre in showStore.genres">
<div v-for="genre in concertStore.genres">
<v-row>
<v-col>
<section-divider

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
import cardWithLeftImage from '@/components/cardWithLeftImage.vue';
import { useConcertStore } from '@/data/stores/concertStore';
import { createDateRangeString, lowestTicketPrice } from '@/scripts/concertScripts';
const concertStore = useConcertStore()
</script>
<template>
<v-container>
<v-row>
<v-spacer />
<v-col cols="10">
<v-row v-for="tour of concertStore.tours">
<v-col>
<card-with-left-image
:title="tour.band.name + ' - ' + tour.name"
:image="'http://localhost:3000/static/tours/' + tour.image"
>
{{ createDateRangeString(tour) }}
<div>{{ tour.shows.length }} {{ $t('tours.concert', tour.shows.length) }}</div>
<template #append>
<div class="d-flex justify-center align-center text-h5" style="height: 100%;">
ab {{ lowestTicketPrice(tour) }}
</div>
</template>
</card-with-left-image>
</v-col>
</v-row>
</v-col>
<v-spacer />
</v-row>
</v-container>
</template>

View File

@@ -1,6 +0,0 @@
<script setup lang="ts">
</script>
<template>
Events
</template>

View File

@@ -1,14 +1,14 @@
<script setup lang="ts">
import sectionDivider from '@/components/sectionDivider.vue';
import { useShowStore } from '@/data/stores/showStore';
import { useConcertStore } from '@/data/stores/concertStore';
import cardWithTopImage from '@/components/cardWithTopImage.vue';
const showStore = useShowStore()
const concertStore = useConcertStore()
</script>
<template>
<v-container>
<div v-for="city in showStore.cities">
<div v-for="city in concertStore.cities">
<v-row>
<v-col>
<section-divider

View File

@@ -1,10 +1,10 @@
import EventsPage from "@/pages/shows/eventsPage/index.vue";
import ConcertsPage from "@/pages/shows/concertsPage/index.vue";
import BandsPage from "@/pages/shows/bandsPage/index.vue";
import LocationsPage from "@/pages/shows/locationsPage/index.vue"
import SearchPage from "@/pages/shows/searchPage/index.vue"
export const showRoutes = [
{ path: '/shows/events', component: EventsPage },
{ path: '/shows/concerts', component: ConcertsPage },
{ path: '/shows/bands', component: BandsPage },
{ path: '/shows/locations', component: LocationsPage },
{ path: '/shows/search', component: SearchPage },

View File

@@ -0,0 +1,65 @@
import { RatingModel } from "@/data/models/ratingModel"
import { dateToHumanReadableString } from "./dateTimeScripts"
import { TourModel } from "@/data/models/tourModel"
/**
* Calculate a price based on parameters
*
* @param price Original price for one unit
* @param discount Discount in percent
* @param quantity Number of units
*
* @returns Price rounded to two digits
*/
export function calcPrice(price: number, discount: number = 0, quantity: number = 1): number {
return Math.round(quantity * price * ((100 - discount) / 100) * 100) / 100
}
/**
* Calculate the average of an Array of ratings
*
* @param ratings Array of ratings
*
* @returns Average rating as number
*/
export function calcRating(ratings: Array<RatingModel>) {
let sum = 0
for (let rating of ratings) {
sum += rating.rating
}
return sum / ratings.length
}
export function createDateRangeString(tour: TourModel) {
const dateArray = []
for (let concert of tour.shows) {
dateArray.push(new Date(concert.date))
}
dateArray.sort(function (a, b) {
return a - b
})
if (dateArray.length > 1) {
return dateToHumanReadableString(dateArray[0]) + ' - ' +
dateToHumanReadableString(dateArray[dateArray.length - 1])
} else {
return dateToHumanReadableString(dateArray[0])
}
}
export function lowestTicketPrice(tour: TourModel): string {
const priceArray : Array<number> = []
for (let concert of tour.shows) {
priceArray.push(concert.price)
}
priceArray.sort()
return priceArray[0].toFixed(2)
}

View File

@@ -0,0 +1,5 @@
export function dateToHumanReadableString(date: Date) {
return String(date.getDate()).padStart(2, '0') + '.' +
String(date.getMonth() + 1).padStart(2, '0') + '.' +
date.getFullYear()
}

View File

@@ -1,31 +0,0 @@
import { RatingModel } from "@/data/models/ratingModel"
/**
* Calculate a price based on parameters
*
* @param price Original price for one unit
* @param discount Discount in percent
* @param quantity Number of units
*
* @returns Price rounded to two digits
*/
export function calcPrice(price: number, discount: number = 0, quantity: number = 1): number {
return Math.round(quantity * price * ((100 - discount) / 100) * 100) / 100
}
/**
* Calculate the average of an Array of ratings
*
* @param ratings Array of ratings
*
* @returns Average rating as number
*/
export function calcRating(ratings: Array<RatingModel>) {
let sum = 0
for (let rating of ratings) {
sum += rating.rating
}
return sum / ratings.length
}