New locationDetailPage displays concert in a location, new datasets, images, URL path changed

This commit is contained in:
2024-09-29 21:42:20 +02:00
parent be5cc090fe
commit d6997229c4
39 changed files with 493 additions and 515 deletions

View File

@@ -426,6 +426,59 @@
"bandId": 6
}
]
},
{
"id": 7,
"name": "Muse",
"foundingYear": 1994,
"descriptionEn": "Muse are an English rock band from Teignmouth, Devon, formed in 1994. The band consists of Matt Bellamy (lead vocals, guitar, keyboards), Chris Wolstenholme (bass guitar, backing vocals), and Dominic Howard (drums, percussion).",
"descriptionDe": "Muse ist eine britische Rockband, die 1994 in Teignmouth, England gegründet wurde. Die Band besteht aus Matthew Bellamy (Gesang, Gitarre, Klavier und Synthesizers), Chris Wolstenholme (E-Bass, Gesang, Synthesizer) und Dominic Howard (Schlagzeug und Perkussion). Muse verbindet stilistisch Alternative, Hard und Progressive Rock sowie Electronica mit Elementen klassischer Musik zu Rockballaden und wird dem Subgenre New Prog zugeordnet.",
"images": [
"muse-1.jpg",
"muse-2.jpg",
"muse-3.jpg"
],
"imageMembers": "muse-members.jpg",
"logo": "muse-logo.jpg",
"genreId": [
2,
15,
16
],
"members": [
{
"name": "Matthew Bellamy",
"bandId": 7,
"image": "matthew-bellamy.jpg"
},
{
"name": "Dominic Howard",
"bandId": 7,
"image": "dominic-howard.jpg"
},
{
"name": "Chris Wolstenholme",
"bandId": 7,
"image": "christ-wolstenholme.jpg"
}
],
"ratings": [
{
"accountId": 0,
"rating": 5,
"bandId": 7
},
{
"accountId": 1,
"rating": 4,
"bandId": 7
},
{
"accountId": 2,
"rating": 4,
"bandId": 7
}
]
}
]
}

View File

@@ -4,7 +4,6 @@
"id": 0,
"name": "Hannover",
"country": "Germany",
"image": "hannover.jpg",
"locations": [
{
"id": 0,
@@ -40,7 +39,6 @@
"id": 1,
"name": "München",
"country": "Germany",
"image": "munich.jpg",
"locations": [
{
"id": 4,
@@ -69,7 +67,6 @@
"id": 2,
"name": "Hamburg",
"country": "Germany",
"image": "hamburg.jpg",
"locations": [
{
"id": 7,
@@ -105,7 +102,6 @@
"id": 3,
"name": "Berlin",
"country": "Germany",
"image": "berlin.jpg",
"locations": [
{
"id": 11,

View File

@@ -59,6 +59,14 @@
{
"id": 14,
"name": "Bluesrock"
},
{
"id": 15,
"name": "New Prog"
},
{
"id": 16,
"name": "Hard Rock"
}
]
}

View File

@@ -11,12 +11,20 @@
"id": 0,
"date": "2024-10-18",
"price": 92,
"inStock": 930,
"inStock": 0,
"locationId": 0,
"tourId": 0
},
{
"id": 1,
"date": "2024-10-19",
"price": 92,
"inStock": 170,
"locationId": 0,
"tourId": 0
},
{
"id": 2,
"date": "2024-10-23",
"price": 119.90,
"inStock": 8736,
@@ -24,7 +32,7 @@
"tourId": 0
},
{
"id": 2,
"id": 3,
"date": "2024-10-26",
"price": 114.90,
"inStock": 2793,
@@ -32,7 +40,7 @@
"tourId": 0
},
{
"id": 3,
"id": 4,
"date": "2024-11-02",
"price": 124.90,
"inStock": 3079,
@@ -49,7 +57,7 @@
"image": "the-bends-tour.jpg",
"concerts": [
{
"id": 4,
"id": 5,
"date": "2024-11-30",
"price": 108,
"inStock": 1200,
@@ -57,7 +65,7 @@
"tourId": 1
},
{
"id": 5,
"id": 6,
"date": "2024-12-01",
"price": 104,
"inStock": 1800,
@@ -65,7 +73,7 @@
"tourId": 1
},
{
"id": 6,
"id": 7,
"date": "2024-12-07",
"price": 99.90,
"inStock": 2438,
@@ -82,7 +90,7 @@
"image": "european-tour-arctic-monkeys.jpg",
"concerts": [
{
"id": 7,
"id": 8,
"date": "2025-01-21",
"price": 67.90,
"inStock": 994,
@@ -90,7 +98,7 @@
"tourId": 2
},
{
"id": 8,
"id": 9,
"date": "2024-11-15",
"price": 79.90,
"inStock": 1073,
@@ -107,7 +115,7 @@
"image": "music-of-the-spheres.png",
"concerts": [
{
"id": 9,
"id": 10,
"date": "2024-12-07",
"price": 124.90,
"inStock": 765,
@@ -115,7 +123,7 @@
"tourId": 3
},
{
"id": 10,
"id": 11,
"date": "2025-01-17",
"price": 129.90,
"inStock": 989,
@@ -123,7 +131,7 @@
"tourId": 3
},
{
"id": 11,
"id": 12,
"date": "2025-02-01",
"price": 134.90,
"inStock": 827,
@@ -140,7 +148,7 @@
"image": "but-here-we-are.jpg",
"concerts": [
{
"id": 12,
"id": 13,
"date": "2024-12-05",
"price": 80,
"inStock": 99,
@@ -157,7 +165,7 @@
"image": "crisis-of-faith-tour.jpg",
"concerts": [
{
"id": 13,
"id": 14,
"date": "2025-01-12",
"price": 81.90,
"inStock": 173,
@@ -165,7 +173,7 @@
"tourId": 5
},
{
"id": 14,
"id": 15,
"date": "2025-02-01",
"price": 84.90,
"inStock": 192,
@@ -182,7 +190,7 @@
"image": "back-to-the-water-below.jpg",
"concerts": [
{
"id": 15,
"id": 16,
"date": "2025-02-27",
"price": 67.90,
"inStock": 847,
@@ -190,7 +198,7 @@
"tourId": 6
},
{
"id": 16,
"id": 17,
"date": "2025-03-06",
"price": 64.90,
"inStock": 245,
@@ -198,6 +206,47 @@
"tourId": 6
}
]
},
{
"id": 7,
"name": "Will of the People Tour",
"bandId": 7,
"offered": true,
"image": "will-of-the-people-tour.jpg",
"concerts": [
{
"id": 18,
"date": "2025-01-15",
"price": 67.90,
"inStock": 847,
"locationId": 2,
"tourId": 7
},
{
"id": 19,
"date": "2025-01-23",
"price": 64.90,
"inStock": 245,
"locationId": 10,
"tourId": 7
},
{
"id": 20,
"date": "2025-02-02",
"price": 64.90,
"inStock": 245,
"locationId": 13,
"tourId": 7
},
{
"id": 21,
"date": "2025-02-05",
"price": 64.90,
"inStock": 245,
"locationId": 13,
"tourId": 7
}
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

View File

@@ -9,9 +9,6 @@ export class City extends Model {
@Column
country: String
@Column
image: String
// Relations

View File

@@ -21,7 +21,7 @@ export class Location extends Model {
// Relations
@HasMany(() => Concert)
shows: Concert[]
concerts: Concert[]
@BelongsTo(() => City)
city: City

View File

@@ -1,14 +1,26 @@
import { Location } from "../models/acts/location.model";
import { City } from "../models/acts/city.model";
import { Request, Response, Router } from "express";
import { Concert } from "../models/acts/concert.model";
export const city = Router()
city.get("/", (req: Request, res: Response) => {
City.findAll({
include: [ Location ]
include: [
{
model: Location,
include: [ Concert ]
}
]
})
.then(cities => {
for (let city of cities) {
for (let location of city.dataValues.locations) {
location.dataValues.nrOfConcerts = location.dataValues.concerts.length
delete location.dataValues.concerts
}
}
res.status(200).json(cities)
})
})

View File

@@ -1,17 +1,41 @@
import { Concert } from "../models/acts/concert.model";
import { City } from "../models/acts/city.model";
import { Location } from "../models/acts/location.model";
import { Request, Response, Router } from "express";
import { Tour } from "../models/acts/tour.model";
import { Band } from "../models/acts/band.model";
export const location = Router()
location.get("/", (req: Request, res: Response) => {
Location.findAll({
include: [ City ],
include: [
City,
{
model: Concert,
include: [ Tour ],
attributes: {
exclude: [ "locationId", "tourId" ]
}
}
],
attributes: {
exclude: [ "cityId" ]
}
})
.then(locations => {
.then(async locations => {
for (let location of locations) {
for (let concert of location.dataValues.concerts) {
let tour = concert.dataValues.tour
await Band.findByPk(tour.dataValues.bandId)
.then(band => {
tour.dataValues.bandName = band.dataValues.name
delete tour.dataValues.bandId
})
}
}
res.status(200).json(locations)
})
})

View File

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

View File

@@ -1,3 +1,6 @@
/**
* Replica of the API endpoint /cities
*/
export class CityModel {
id: number
name: string
@@ -8,5 +11,6 @@ export class CityModel {
name: string
address: string
image: string
nrOfConcerts: number
}>
}

View File

@@ -1,3 +1,6 @@
/**
* Replica of the API endpoint /locations
*/
export class LocationModel {
id: number
name: string
@@ -7,4 +10,16 @@ export class LocationModel {
name: string
country: string
}
concerts: Array<{
id: number
date: string
price: number
inStock: number
tour: {
name: string
offered: boolean
image: string
bandName: string
}
}>
}

View File

@@ -53,6 +53,10 @@ export const useConcertStore = defineStore("concertStore", {
await getAllGenres()
.then(result => {
this.genres = result.data
this.genres.sort((a, b) => {
return a.name > b.name
})
})
await getAllCities()

View File

@@ -1,19 +1,10 @@
{
"menu": {
"allConcerts": "Alle Konzerte",
"allLocations": "Alle Veranstaltungsorte",
"allBands": "Alle Bands"
},
"shows": {
"highlights": "Highlights",
"tickets": "Ticket | Tickets",
"topConcerts": "Top Konzerte",
"topBands": "Top Bands",
"topLocations": "Top Veranstaltungsorte"
},
"tours": {
"concert": "Konzert | Konzerte"
},
"allEvents": "Alle Events",
"allLocations": "Alle Veranstaltungsorte",
"topEvents": "Top Events",
"topLocations": "Top Veranstaltungsorte",
"tickets": "Ticket | Tickets",
"concert": "Konzert | Konzerte",
"preferences": {
"pageSetup": "Seiteneinstellungen",
"selectedTheme": "Ausgewähltes Theme",
@@ -143,5 +134,10 @@
"brand": "Marke | Marken",
"concerts": {
"location": "Veranstaltungsort"
}
},
"location": "Veranstaltungsort | Veranstaltungsorte",
"band": "Band | Bands",
"noEventsFound": "Keine Events gefunden",
"from": "ab",
"soldOut": "Ausverkauft"
}

View File

@@ -1,19 +1,10 @@
{
"menu": {
"allConcerts": "All Concerts",
"allLocations": "All Locations",
"allBands": "All Bands"
},
"shows": {
"highlights": "Highlights",
"tickets": "Ticket | Tickets",
"topConcerts": "Top Concerts",
"topBands": "Top Bands",
"topLocations": "Top Locations"
},
"tours": {
"concert": "Concert | Concerts"
},
"allEvents": "All Events",
"allLocations": "All Locations",
"topEvents": "Top Events",
"topLocations": "Top Locations",
"tickets": "Ticket | Tickets",
"concert": "Concert | Concerts",
"preferences": {
"pageSetup": "Page setup",
"selectedTheme": "Selected theme",
@@ -143,5 +134,10 @@
"brand": "Brand | Brands",
"concerts": {
"location": "Locations"
}
},
"location": "Location | Locations",
"band": "Band | Bands",
"noEventsFound": "No Events found",
"from": "from",
"soldOut": "Sold Out"
}

View File

@@ -51,9 +51,9 @@ const router = useRouter()
append-icon="mdi-arrow-right"
class="mt-2"
color="primary"
@click="router.push('shows/band/red-hot-chili-peppers')"
@click="router.push('bands/' + band.name.replaceAll(' ', '-').toLowerCase())"
>
{{ $t('shows.tickets', 2) }}
{{ $t('tickets', 2) }}
</outlined-button>
</v-card-text>
</v-card>

View File

@@ -16,63 +16,75 @@ const concertStore = useConcertStore()
<v-container>
<v-row>
<v-col>
<section-divider :title="$t('shows.highlights')" />
</v-col>
</v-row>
<v-spacer />
<v-row>
<v-col v-for="i in 6" cols="2">
<card-with-top-image
:image="'tours/' + concertStore.tours[i - 1].image"
:title="concertStore.tours[i - 1].band.name"
smaller-title
>
Tickets ab {{ lowestTicketPrice(concertStore.tours[i - 1]) }}
</card-with-top-image>
</v-col>
</v-row>
<v-col cols="10">
<v-row>
<v-col>
<section-divider :title="$t('topEvents')" />
</v-col>
</v-row>
<v-row>
<v-col>
<outlined-button
append-icon="mdi-chevron-right"
@click="router.push('/shows/concerts')"
block
>
{{ $t('menu.allConcerts') }}
</outlined-button>
</v-col>
</v-row>
<v-row>
<v-col v-for="i in 4" cols="3">
<card-with-top-image
:image="'tours/' + concertStore.tours[i - 1].image"
:title="concertStore.tours[i - 1].band.name"
smaller-title
@click="router.push('/bands/' + concertStore.tours[i - 1].band.name.replaceAll(' ', '-').toLowerCase())"
>
ab {{ lowestTicketPrice(concertStore.tours[i - 1]) }}
</card-with-top-image>
</v-col>
</v-row>
<v-row>
<v-col>
<section-divider :title="$t('shows.topLocations')" />
</v-col>
</v-row>
<v-row>
<v-col>
<outlined-button
append-icon="mdi-chevron-right"
@click="router.push('/events')"
block
>
{{ $t('allEvents') }}
</outlined-button>
</v-col>
</v-row>
<v-row>
<v-col v-for="i in 6" cols="2">
<card-with-top-image
:image="'locations/' + concertStore.locations[i + 2].image"
:title="concertStore.locations[i + 2].name"
smaller-title
>
{{ concertStore.locations[i + 2].city.name }}, {{ concertStore.locations[i + 2].city.country }}
</card-with-top-image>
</v-col>
</v-row>
<v-row>
<v-col>
<section-divider :title="$t('topLocations')" />
</v-col>
</v-row>
<v-row>
<v-col>
<outlined-button
append-icon="mdi-chevron-right"
@click="router.push('/shows/locations')"
block
>
{{ $t('menu.allLocations') }}
</outlined-button>
<v-row>
<v-col v-for="i in 8" cols="3">
<card-with-top-image
:image="'locations/' + concertStore.locations[i + 2].image"
:title="concertStore.locations[i + 2].name"
smaller-title
>
{{ concertStore.locations[i + 2].city.name }}, {{ concertStore.locations[i + 2].city.country }}
</card-with-top-image>
</v-col>
</v-row>
<v-row>
<v-col>
<outlined-button
append-icon="mdi-chevron-right"
@click="router.push('/locations')"
block
>
{{ $t('allLocations') }}
</outlined-button>
</v-col>
</v-row>
</v-col>
<v-spacer />
</v-row>
</v-container>
</template>

View File

@@ -0,0 +1,91 @@
<script setup lang="ts">
import { LocationModel } from '@/data/models/acts/locationModel';
import { useConcertStore } from '@/data/stores/concertStore';
import { createDateRangeString, lowestTicketPrice } from '@/scripts/concertScripts';
import { useRouter } from 'vue-router';
import cardWithLeftImage from '@/components/cardWithLeftImage.vue';
import sectionDivider from '@/components/sectionDivider.vue';
import { dateStringToHumanReadableString } from '@/scripts/dateTimeScripts';
const router = useRouter()
const concertStore = useConcertStore()
const location: LocationModel = concertStore.locations.find(location =>
location.name.replaceAll(' ', '-').toLowerCase() == router.currentRoute.value.params.locationName
)
</script>
<template>
<div class="position-relative top-0 left-0">
<v-img
:src="'http://localhost:3000/static/locations/' + location.image"
height="500"
gradient="to top, rgba(0, 0, 0, .9), rgba(255, 255, 255, 0.1)"
cover
>
<div class="position-absolute bottom-0 pa-5">
<v-row>
<v-col>
<p class="text-h3">{{ location.name }}</p>
<p class="text-h6">{{ location.address }}</p>
<p class="text-h6">{{ location.city.name }}</p>
</v-col>
</v-row>
</div>
</v-img>
</div>
<v-container>
<v-row>
<v-spacer/>
<v-col cols="10">
<v-row>
<v-col>
<section-divider title="Konzerte" />
</v-col>
</v-row>
<v-row
v-if="location.concerts.length > 0"
v-for="concert of location.concerts"
>
<v-col>
<card-with-left-image
:title="concert.tour.bandName + ' - ' + concert.tour.name"
:image="'http://localhost:3000/static/tours/' + concert.tour.image"
@click="router.push('/bands/' + concert.tour.bandName.replaceAll(' ', '-').toLowerCase())"
>
<div class="text-h6">
{{ dateStringToHumanReadableString(concert.date) }}
</div>
<!-- <div>{{ concert.length }} {{ $t('concert', concert.tour.concerts.length) }}</div> -->
<template #append>
<div>
<v-icon
icon="mdi-ticket"
color="secondary"
size="x-large"
/>
</div>
{{ $t('from') }} {{ concert.price.toFixed(2) }}
</template>
</card-with-left-image>
</v-col>
</v-row>
<v-row v-else>
<v-col>
<v-empty-state
icon="mdi-magnify"
:title="$t('noEventsFound')"
/>
</v-col>
</v-row>
</v-col>
<v-spacer/>
</v-row>
</v-container>
</template>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import sectionDivider from '@/components/sectionDivider.vue';
import { useConcertStore } from '@/data/stores/concertStore';
import cardWithTopImage from '@/components/cardWithTopImage.vue';
import { useRouter } from 'vue-router';
const concertStore = useConcertStore()
const router = useRouter()
</script>
<template>
<v-container>
<v-row>
<v-spacer />
<v-col cols="10">
<div v-for="city in concertStore.cities">
<v-row>
<v-col>
<section-divider
:title="city.name"
/>
</v-col>
</v-row>
<v-row>
<v-col v-for="location in city.locations" cols="3">
<card-with-top-image
:image="'locations/' + location.image"
:title="location.name"
@click="router.push('locations/' + location.name.replaceAll(' ', '-').toLowerCase())"
>
<div>
{{ location.nrOfConcerts }} {{ $t('concert', location.nrOfConcerts) }}
</div>
</card-with-top-image>
</v-col>
</v-row>
</div>
</v-col>
<v-spacer />
</v-row>
</v-container>
</template>

View File

@@ -32,6 +32,15 @@ defineProps({
<v-col>
<p class="text-h3">{{ band.name }}</p>
<div>
<v-chip
v-for="genre in band.genres"
class="mr-2 my-1"
variant="flat"
>
{{ genre.name }}
</v-chip>
</div>
<p class="text-h6">{{ band.descriptionDe }}</p>
</v-col>
</v-row>

View File

@@ -4,6 +4,9 @@ import { dateStringToHumanReadableString } from '@/scripts/dateTimeScripts';
import sectionDivider from '@/components/sectionDivider.vue';
import cardWithLeftImage from '@/components/cardWithLeftImage.vue';
import outlinedButton from '@/components/outlinedButton.vue';
import { useRouter } from 'vue-router';
const router = useRouter()
defineProps({
band: {
@@ -27,17 +30,26 @@ defineProps({
:image="'http://localhost:3000/static/locations/' + concert.location.image"
:link="false"
>
<div>
{{ concert.location.name }}
</div>
<v-row>
<v-col cols="auto" class="d-flex justify-center align-center px-0">
<v-btn
icon="mdi-map"
variant="text"
size="x-large"
@click="router.push('/locations/' + concert.location.name.replaceAll(' ', '-').toLowerCase())"
/>
</v-col>
<div>
{{ concert.location.address }}
</div>
<v-col>
<div class="text-h6">
{{ concert.location.name }}
</div>
<div>
{{ concert.location.city.name }}
</div>
<div class="text-h6">
{{ concert.location.city.name }}
</div>
</v-col>
</v-row>
<template #append>
<div class="pb-3">
@@ -46,11 +58,19 @@ defineProps({
<div>
<outlined-button
v-if="concert.inStock > 0"
prepend-icon="mdi-basket-plus"
>
Hinzufügen
{{ $t('add') }}
</outlined-button>
</div>
<outlined-button v-else
color="red"
disabled
>
{{ $t('soldOut') }}
</outlined-button>
</div>
</template>
</card-with-left-image>
</v-col>

View File

@@ -23,7 +23,10 @@ const concertStore = useConcertStore()
color="secondary"
>
<template #item="{ props, item }">
<v-list-item v-bind="props" :title="item.raw.name" />
<v-list-item
v-bind="props"
:title="item.raw.name + ' (' + item.raw.locations.length + ' ' +
$t('location', item.raw.locations.length) + ')'" />
</template>
<template #selection="{ item }">
@@ -41,7 +44,7 @@ const concertStore = useConcertStore()
variant="outlined"
:items="concertStore.filteredLocations"
v-model="concertStore.locationFilter"
:label="$t('concerts.location')"
:label="$t('location', 2)"
density="compact"
:clearable="concertStore.locationFilter != null && concertStore.locationFilter.id != undefined"
:disabled="concertStore.cityFilter == null || concertStore.cityFilter.id == undefined"
@@ -50,7 +53,10 @@ const concertStore = useConcertStore()
color="secondary"
>
<template #item="{ props, item }">
<v-list-item v-bind="props" :title="item.raw.name" />
<v-list-item
v-bind="props"
:title="item.raw.name + ' (' + item.raw.concerts + ' Locations)'"
/>
</template>
<template #selection="{ item }">
@@ -74,7 +80,9 @@ const concertStore = useConcertStore()
color="secondary"
>
<template #item="{ props, item }">
<v-list-item v-bind="props" :title="item.raw.name" />
<v-list-item v-bind="props" :title="item.raw.name + ' (' + item.raw.bands.length +
' ' + $t('band', item.raw.bands.length) + ')'"
/>
</template>
<template #selection="{ item }">

View File

@@ -29,12 +29,19 @@ const concertStore = useConcertStore()
<card-with-left-image
:title="tour.band.name + ' - ' + tour.name"
:image="'http://localhost:3000/static/tours/' + tour.image"
@click="router.push('/shows/band/' + tour.band.name.replaceAll(' ', '-').toLowerCase())"
@click="router.push('/bands/' + tour.band.name.replaceAll(' ', '-').toLowerCase())"
>
{{ createDateRangeString(tour) }}
<div>{{ tour.concerts.length }} {{ $t('tours.concert', tour.concerts.length) }}</div>
<div>{{ tour.concerts.length }} {{ $t('concert', tour.concerts.length) }}</div>
<template #append>
<div>
<v-icon
icon="mdi-ticket"
color="secondary"
size="x-large"
/>
</div>
ab {{ lowestTicketPrice(tour) }}
</template>
</card-with-left-image>
@@ -44,7 +51,7 @@ const concertStore = useConcertStore()
<v-row v-else>
<v-col>
<v-empty-state
title="Keine Konzerte gefunden"
:title="$t('noEventsFound')"
icon="mdi-magnify"
/>
</v-col>

View File

@@ -1,32 +0,0 @@
<script setup lang="ts">
import sectionDivider from '@/components/sectionDivider.vue';
import { useConcertStore } from '@/data/stores/concertStore';
import cardWithTopImage from '@/components/cardWithTopImage.vue';
const concertStore = useConcertStore()
</script>
<template>
<v-container>
<div v-for="city in concertStore.cities">
<v-row>
<v-col>
<section-divider
:title="city.name"
/>
</v-col>
</v-row>
<v-row>
<v-col v-for="location in city.locations" cols="3">
<card-with-top-image
:image="'locations/' + location.image"
:title="location.name"
>
{{ location.address }}
</card-with-top-image>
</v-col>
</v-row>
</div>
</v-container>
</template>

View File

@@ -1,43 +0,0 @@
<script setup lang="ts">
import { SortOrder } from '@/data/enums/sortOrderEnum';
const sortOrderItems = Object.values(SortOrder)
</script>
<template>
<v-navigation-drawer location="right" width="300" permanent>
<v-list>
<v-list-subheader>Filter</v-list-subheader>
<!-- <v-list-item>
<v-select
:items="tourStore.genres"
:label="$t('category', 2)"
>
<template v-slot:item="{ props, item }">
<v-list-item v-bind="props" :prepend-icon="item.raw.icon" :title="item.raw.name" />
</template>
<template v-slot:selection="{ item }">
<v-list-item :prepend-icon="item.raw.icon" :title="item.raw.name" />
</template>
</v-select>
</v-list-item> -->
<v-divider />
<v-list-subheader>Sort</v-list-subheader>
<!-- <v-list-item>
<v-select :label="$t('sortBy')" :items="sortOrderItems" v-model="productStore.sortOrder" >
<template v-slot:item="{ props, item }">
<v-list-item v-bind="props" :title="item.title" />
</template>
<template v-slot:selection="{ item }">
<v-list-item :title="item.title" />
</template>
</v-select>
</v-list-item> -->
</v-list>
</v-navigation-drawer>
</template>

View File

@@ -1,48 +0,0 @@
<script setup lang="ts">
import tourCard from "./tourCard.vue"
import { ref, watch } from "vue";
import filterNavDrawer from "./filterNavDrawer.vue";
const showProductDetails = ref(false)
// const dialogProduct = ref(new ProductModel())
// function showProductDialog(product: ProductModel) {
// dialogProduct.value = product
// showProductDetails.value = true
// }
// watch(() => productStore.filteredCategory, async () => { productStore.filterProducts() })
// watch(() => productStore.sortOrder, async () => { productStore.sortProducts() })
// watch(() => productStore.onlyDiscounts, async () => { productStore.filterProducts() })
</script>
<template>
<!-- <v-container max-width="1200">
<v-row dense>
<v-col
v-if="tourStore.tours.length > 0"
v-for="tour in tourStore.tours"
cols="12"
>
<tour-card
:tour="tour"
/>
</v-col>
<v-col v-else>
<v-empty-state
icon="mdi-magnify"
headline="Keine Artikel gefunden"
/>
</v-col>
</v-row>
</v-container> -->
<filter-nav-drawer />
<!-- <product-details
v-model="showProductDetails"
:product="dialogProduct"
/> -->
</template>

View File

@@ -1,157 +0,0 @@
<script setup lang="ts">
import { VNumberInput } from 'vuetify/labs/VNumberInput'
import { ModelRef, ref, watch } from 'vue';
import { useBasketStore } from '@/data/stores/basketStore';
import ActionDialog from '@/components/actionDialog.vue'
import outlinedButton from '@/components/outlinedButton.vue';
// const props = defineProps({
// product: {
// type: ProductModel,
// default: new ProductModel()
// }
// })
// const showDialog: ModelRef<boolean> = defineModel()
// const nrOfArticles = ref(1)
// const basketStore = useBasketStore()
// const selectedImage = ref("")
// function addProductToBasket() {
// basketStore.addItemToBasket(props.product, nrOfArticles.value)
// nrOfArticles.value = 1
// showDialog.value = false
// }
// watch(() => props.product.images, () => {
// selectedImage.value = 'http://localhost:3000/static/' + props.product.images[0]
// })
</script>
<template>
<!-- <action-dialog
:title="product.brand.name + ': ' + product.name"
:icon="product.category.icon"
:subtitle="product.category.name"
v-model="showDialog"
>
<v-row>
<v-col>
<v-row>
<v-col class="py-0">
<v-sheet color="white">
<v-img :src="selectedImage" height="600" />
</v-sheet>
</v-col>
</v-row>
<v-row>
<v-spacer />
<v-col v-for="image in product.images">
<v-card
width="60"
@click="selectedImage = 'http://localhost:3000/static/' + image"
color="white"
>
<v-img :src="'http://localhost:3000/static/' + image" height="60" />
</v-card>
</v-col>
<v-spacer />
</v-row>
</v-col>
<v-col>
<v-row>
<v-col class="text-h6 pt-0">
{{ $t("product.description") }}
</v-col>
</v-row>
<v-row class="text-body-1 mt-0" style="height: 450px; overflow-y: auto;">
<div v-if="product.description.length != 0">
<v-col class="text-body-1">
{{ product.description }}
</v-col>
</div>
<div v-if="product.specs.length > 1">
<v-list>
<v-list-item v-for="spec in product.specs">
<v-icon icon="mdi-circle-small" /> {{ spec }}
</v-list-item>
</v-list>
</div>
</v-row>
<v-divider class="my-4" />
<v-row>
<v-col class="pb-0">
<div class="d-flex align-center flex-column">
<div class="text-h3"> {{ product.rating }} <span class="text-h6 ml-n3">/5</span> </div>
<v-rating :model-value="product.rating" color="yellow-darken-3" half-increments disabled />
</div>
</v-col>
</v-row>
<v-divider class="my-4" />
<v-row>
<v-col cols="3">
<div class="pt-3">
<div v-if="product.inStock > 5" class="text-green-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else-if="product.inStock > 0" class="text-orange-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else class="text-red">
{{ $t("product.soldOut") }}
</div>
</div>
</v-col>
<v-col class="d-flex align-end flex-column my-auto">
<div v-if="product.discount == 0" class="text-h3">{{ product.price }} </div>
<div v-else class="d-flex align-center flex-column my-auto">
<div class="text-h3">
<span class="text-red-lighten-1"> {{ calcPrice(product.price, product.discount) }} </span>
<span class="text-h6 ml-2 text-decoration-line-through">{{ product.price }} </span>
<span class="text-h6 ml-4 mb-1 bg-red">-{{ product.discount }} %</span>
</div>
</div>
</v-col>
</v-row>
</v-col>
</v-row>
<template #actions>
<v-number-input
max-width="200"
:reverse="false"
controlVariant="default"
:label="$t('quantity')"
:hideInput="false"
:inset="true"
v-model="nrOfArticles"
variant="outlined"
:min="1"
:max="product.inStock"
density="comfortable"
:hide-details="true"
:disabled="product.inStock == 0"
/>
<outlined-button
prepend-icon="mdi-cart-plus"
@click="addProductToBasket"
height="50"
:disabled="product.inStock == 0"
>
{{ $t('addToBasket') }}
</outlined-button>
</template>
</action-dialog> -->
</template>

View File

@@ -1,88 +0,0 @@
<script setup lang="ts">
import cardView from '@/components/cardView.vue';
import OutlinedButton from '@/components/outlinedButton.vue';
// defineProps({
// tour: {
// required: true,
// type: TourModel
// }
// })
</script>
<template>
<!-- <card-view
:title="tour.band.name"
:subtitle="tour.name"
:prepend-image="'http://127.0.0.1:3000/static/bands/' + tour.band.images[0]"
link
>
{{ tour.band.descriptionDe }}
<template #actions>
<OutlinedButton>
{{ tour.shows.length }} {{ $t('tours.concert', tour.shows.length) }}
</OutlinedButton>
</template> -->
<!-- <template> -->
<!-- <div>
<v-rating
size="medium"
:model-value="product.rating"
active-color="yellow-darken-1"
color="grey-darken-1"
half-increments
readonly
/>
{{ product.rating }}/5
</div> -->
<!-- <div>
<v-list class="pa-0">
<v-list-item v-for="i in 2" class="pa-0 ma-0">
<v-icon icon="mdi-circle-small" /> {{ product.specs[i - 1] }}
</v-list-item>
</v-list>
</div> -->
<!-- </v-col> -->
<!-- <v-col cols="2" class="pt-5 pr-7"> -->
<!-- <div v-if="product.discount == 0" class="font-weight-bold text-h5 text-right">
{{ product.price }}
</div>
<div v-else>
<div class="font-weight-bold text-h5 text-right">
{{ (product.price * ( 100 - product.discount) / 100).toFixed(2) }}
</div>
<div>
<div class="text-decoration-line-through text-right">{{ product.price }} </div>
</div>
</div> -->
<!-- <div style="position: absolute; bottom: 0; right: 0;" class="pr-2 pb-2">
<div v-if="product.inStock > 5" class="text-green-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else-if="product.inStock > 0" class="text-orange-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else class="text-red">
{{ $t("product.soldOut") }}
</div>
</div> -->
<!-- </template> -->
<!-- </card-view> -->
</template>
<style scoped>
.v-sheet {
filter: brightness(60%)
}
.v-card:hover :deep(.v-sheet) {
filter: brightness(80%)
}
</style>

View File

@@ -2,13 +2,21 @@ import BasketPage from "@/pages/basketPage/index.vue"
import HomePage from "@/pages/homePage/index.vue"
import { adminRoutes } from "./admin.routes";
import { accountRoutes } from "./account.routes";
import { showRoutes } from "./show.routes";
import { systemRoutes } from "./system.routes";
import EventsPage from "@/pages/shows/eventsPage/index.vue";
import LocationsPage from "@/pages/locations/locationsPage/index.vue"
import SearchPage from "@/pages/shows/searchPage/index.vue"
import BandDetailPage from "@/pages/shows/bandDetailPage/index.vue"
import LocationDetailPage from "@/pages/locations/locationDetailPage/index.vue"
const routes = [
{ path: "/", component: HomePage },
{ path: '/basket', component: BasketPage },
...showRoutes,
{ path: '/locations', component: LocationsPage },
{ path: '/locations/:locationName', component: LocationDetailPage },
{ path: '/bands/:bandName', component: BandDetailPage },
{ path: '/events', component: EventsPage },
{ path: '/search', component: SearchPage },
...accountRoutes,
...adminRoutes,
...systemRoutes

View File

@@ -1,11 +0,0 @@
import ToursPage from "@/pages/shows/toursPage/index.vue";
import LocationsPage from "@/pages/shows/locationsPage/index.vue"
import SearchPage from "@/pages/shows/searchPage/index.vue"
import BandDetailPage from "@/pages/shows/bandDetailPage/index.vue"
export const showRoutes = [
{ path: '/shows/tours', component: ToursPage },
{ path: '/shows/locations', component: LocationsPage },
{ path: '/shows/search', component: SearchPage },
{ path: '/shows/band/:bandName', component: BandDetailPage }
]