New locationDetailPage displays concert in a location, new datasets, images, URL path changed
@@ -426,6 +426,59 @@
|
|||||||
"bandId": 6
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "Hannover",
|
"name": "Hannover",
|
||||||
"country": "Germany",
|
"country": "Germany",
|
||||||
"image": "hannover.jpg",
|
|
||||||
"locations": [
|
"locations": [
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 0,
|
||||||
@@ -40,7 +39,6 @@
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "München",
|
"name": "München",
|
||||||
"country": "Germany",
|
"country": "Germany",
|
||||||
"image": "munich.jpg",
|
|
||||||
"locations": [
|
"locations": [
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
@@ -69,7 +67,6 @@
|
|||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "Hamburg",
|
"name": "Hamburg",
|
||||||
"country": "Germany",
|
"country": "Germany",
|
||||||
"image": "hamburg.jpg",
|
|
||||||
"locations": [
|
"locations": [
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
@@ -105,7 +102,6 @@
|
|||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "Berlin",
|
"name": "Berlin",
|
||||||
"country": "Germany",
|
"country": "Germany",
|
||||||
"image": "berlin.jpg",
|
|
||||||
"locations": [
|
"locations": [
|
||||||
{
|
{
|
||||||
"id": 11,
|
"id": 11,
|
||||||
|
|||||||
@@ -59,6 +59,14 @@
|
|||||||
{
|
{
|
||||||
"id": 14,
|
"id": 14,
|
||||||
"name": "Bluesrock"
|
"name": "Bluesrock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"name": "New Prog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"name": "Hard Rock"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,20 @@
|
|||||||
"id": 0,
|
"id": 0,
|
||||||
"date": "2024-10-18",
|
"date": "2024-10-18",
|
||||||
"price": 92,
|
"price": 92,
|
||||||
"inStock": 930,
|
"inStock": 0,
|
||||||
"locationId": 0,
|
"locationId": 0,
|
||||||
"tourId": 0
|
"tourId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
"date": "2024-10-19",
|
||||||
|
"price": 92,
|
||||||
|
"inStock": 170,
|
||||||
|
"locationId": 0,
|
||||||
|
"tourId": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
"date": "2024-10-23",
|
"date": "2024-10-23",
|
||||||
"price": 119.90,
|
"price": 119.90,
|
||||||
"inStock": 8736,
|
"inStock": 8736,
|
||||||
@@ -24,7 +32,7 @@
|
|||||||
"tourId": 0
|
"tourId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 3,
|
||||||
"date": "2024-10-26",
|
"date": "2024-10-26",
|
||||||
"price": 114.90,
|
"price": 114.90,
|
||||||
"inStock": 2793,
|
"inStock": 2793,
|
||||||
@@ -32,7 +40,7 @@
|
|||||||
"tourId": 0
|
"tourId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 4,
|
||||||
"date": "2024-11-02",
|
"date": "2024-11-02",
|
||||||
"price": 124.90,
|
"price": 124.90,
|
||||||
"inStock": 3079,
|
"inStock": 3079,
|
||||||
@@ -49,7 +57,7 @@
|
|||||||
"image": "the-bends-tour.jpg",
|
"image": "the-bends-tour.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 5,
|
||||||
"date": "2024-11-30",
|
"date": "2024-11-30",
|
||||||
"price": 108,
|
"price": 108,
|
||||||
"inStock": 1200,
|
"inStock": 1200,
|
||||||
@@ -57,7 +65,7 @@
|
|||||||
"tourId": 1
|
"tourId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 6,
|
||||||
"date": "2024-12-01",
|
"date": "2024-12-01",
|
||||||
"price": 104,
|
"price": 104,
|
||||||
"inStock": 1800,
|
"inStock": 1800,
|
||||||
@@ -65,7 +73,7 @@
|
|||||||
"tourId": 1
|
"tourId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 6,
|
"id": 7,
|
||||||
"date": "2024-12-07",
|
"date": "2024-12-07",
|
||||||
"price": 99.90,
|
"price": 99.90,
|
||||||
"inStock": 2438,
|
"inStock": 2438,
|
||||||
@@ -82,7 +90,7 @@
|
|||||||
"image": "european-tour-arctic-monkeys.jpg",
|
"image": "european-tour-arctic-monkeys.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 8,
|
||||||
"date": "2025-01-21",
|
"date": "2025-01-21",
|
||||||
"price": 67.90,
|
"price": 67.90,
|
||||||
"inStock": 994,
|
"inStock": 994,
|
||||||
@@ -90,7 +98,7 @@
|
|||||||
"tourId": 2
|
"tourId": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 8,
|
"id": 9,
|
||||||
"date": "2024-11-15",
|
"date": "2024-11-15",
|
||||||
"price": 79.90,
|
"price": 79.90,
|
||||||
"inStock": 1073,
|
"inStock": 1073,
|
||||||
@@ -107,7 +115,7 @@
|
|||||||
"image": "music-of-the-spheres.png",
|
"image": "music-of-the-spheres.png",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 9,
|
"id": 10,
|
||||||
"date": "2024-12-07",
|
"date": "2024-12-07",
|
||||||
"price": 124.90,
|
"price": 124.90,
|
||||||
"inStock": 765,
|
"inStock": 765,
|
||||||
@@ -115,7 +123,7 @@
|
|||||||
"tourId": 3
|
"tourId": 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 10,
|
"id": 11,
|
||||||
"date": "2025-01-17",
|
"date": "2025-01-17",
|
||||||
"price": 129.90,
|
"price": 129.90,
|
||||||
"inStock": 989,
|
"inStock": 989,
|
||||||
@@ -123,7 +131,7 @@
|
|||||||
"tourId": 3
|
"tourId": 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 11,
|
"id": 12,
|
||||||
"date": "2025-02-01",
|
"date": "2025-02-01",
|
||||||
"price": 134.90,
|
"price": 134.90,
|
||||||
"inStock": 827,
|
"inStock": 827,
|
||||||
@@ -140,7 +148,7 @@
|
|||||||
"image": "but-here-we-are.jpg",
|
"image": "but-here-we-are.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 12,
|
"id": 13,
|
||||||
"date": "2024-12-05",
|
"date": "2024-12-05",
|
||||||
"price": 80,
|
"price": 80,
|
||||||
"inStock": 99,
|
"inStock": 99,
|
||||||
@@ -157,7 +165,7 @@
|
|||||||
"image": "crisis-of-faith-tour.jpg",
|
"image": "crisis-of-faith-tour.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 13,
|
"id": 14,
|
||||||
"date": "2025-01-12",
|
"date": "2025-01-12",
|
||||||
"price": 81.90,
|
"price": 81.90,
|
||||||
"inStock": 173,
|
"inStock": 173,
|
||||||
@@ -165,7 +173,7 @@
|
|||||||
"tourId": 5
|
"tourId": 5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 14,
|
"id": 15,
|
||||||
"date": "2025-02-01",
|
"date": "2025-02-01",
|
||||||
"price": 84.90,
|
"price": 84.90,
|
||||||
"inStock": 192,
|
"inStock": 192,
|
||||||
@@ -182,7 +190,7 @@
|
|||||||
"image": "back-to-the-water-below.jpg",
|
"image": "back-to-the-water-below.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 15,
|
"id": 16,
|
||||||
"date": "2025-02-27",
|
"date": "2025-02-27",
|
||||||
"price": 67.90,
|
"price": 67.90,
|
||||||
"inStock": 847,
|
"inStock": 847,
|
||||||
@@ -190,7 +198,7 @@
|
|||||||
"tourId": 6
|
"tourId": 6
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 16,
|
"id": 17,
|
||||||
"date": "2025-03-06",
|
"date": "2025-03-06",
|
||||||
"price": 64.90,
|
"price": 64.90,
|
||||||
"inStock": 245,
|
"inStock": 245,
|
||||||
@@ -198,6 +206,47 @@
|
|||||||
"tourId": 6
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
BIN
software/backend/images/bands/muse-1.jpg
Normal file
|
After Width: | Height: | Size: 425 KiB |
BIN
software/backend/images/bands/muse-2.jpg
Normal file
|
After Width: | Height: | Size: 292 KiB |
BIN
software/backend/images/bands/muse-3.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
software/backend/images/bands/muse-logo.jpg
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
software/backend/images/bands/muse-members.jpg
Normal file
|
After Width: | Height: | Size: 470 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 426 KiB |
|
Before Width: | Height: | Size: 214 KiB |
BIN
software/backend/images/tours/will-of-the-people-tour.jpg
Normal file
|
After Width: | Height: | Size: 250 KiB |
@@ -9,9 +9,6 @@ export class City extends Model {
|
|||||||
@Column
|
@Column
|
||||||
country: String
|
country: String
|
||||||
|
|
||||||
@Column
|
|
||||||
image: String
|
|
||||||
|
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class Location extends Model {
|
|||||||
// Relations
|
// Relations
|
||||||
|
|
||||||
@HasMany(() => Concert)
|
@HasMany(() => Concert)
|
||||||
shows: Concert[]
|
concerts: Concert[]
|
||||||
|
|
||||||
@BelongsTo(() => City)
|
@BelongsTo(() => City)
|
||||||
city: City
|
city: City
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
import { Location } from "../models/acts/location.model";
|
import { Location } from "../models/acts/location.model";
|
||||||
import { City } from "../models/acts/city.model";
|
import { City } from "../models/acts/city.model";
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
import { Concert } from "../models/acts/concert.model";
|
||||||
|
|
||||||
export const city = Router()
|
export const city = Router()
|
||||||
|
|
||||||
city.get("/", (req: Request, res: Response) => {
|
city.get("/", (req: Request, res: Response) => {
|
||||||
City.findAll({
|
City.findAll({
|
||||||
include: [ Location ]
|
include: [
|
||||||
|
{
|
||||||
|
model: Location,
|
||||||
|
include: [ Concert ]
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
.then(cities => {
|
.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)
|
res.status(200).json(cities)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1,17 +1,41 @@
|
|||||||
|
import { Concert } from "../models/acts/concert.model";
|
||||||
import { City } from "../models/acts/city.model";
|
import { City } from "../models/acts/city.model";
|
||||||
import { Location } from "../models/acts/location.model";
|
import { Location } from "../models/acts/location.model";
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
import { Tour } from "../models/acts/tour.model";
|
||||||
|
import { Band } from "../models/acts/band.model";
|
||||||
|
|
||||||
export const location = Router()
|
export const location = Router()
|
||||||
|
|
||||||
location.get("/", (req: Request, res: Response) => {
|
location.get("/", (req: Request, res: Response) => {
|
||||||
Location.findAll({
|
Location.findAll({
|
||||||
include: [ City ],
|
include: [
|
||||||
|
City,
|
||||||
|
{
|
||||||
|
model: Concert,
|
||||||
|
include: [ Tour ],
|
||||||
|
attributes: {
|
||||||
|
exclude: [ "locationId", "tourId" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: [ "cityId" ]
|
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)
|
res.status(200).json(locations)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -10,12 +10,12 @@
|
|||||||
<v-divider vertical />
|
<v-divider vertical />
|
||||||
|
|
||||||
<v-btn
|
<v-btn
|
||||||
to="/shows/tours"
|
to="/events"
|
||||||
prepend-icon="mdi-ticket"
|
prepend-icon="mdi-ticket"
|
||||||
height="100%"
|
height="100%"
|
||||||
:rounded="false"
|
:rounded="false"
|
||||||
>
|
>
|
||||||
{{ $t('menu.allConcerts', 2) }}
|
{{ $t('allEvents', 2) }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-divider vertical />
|
<v-divider vertical />
|
||||||
@@ -23,12 +23,12 @@
|
|||||||
|
|
||||||
<v-btn
|
<v-btn
|
||||||
variant="text"
|
variant="text"
|
||||||
to="/shows/locations"
|
to="/locations"
|
||||||
prepend-icon="mdi-city"
|
prepend-icon="mdi-city"
|
||||||
height="100%"
|
height="100%"
|
||||||
:rounded="false"
|
:rounded="false"
|
||||||
>
|
>
|
||||||
{{ $t('menu.allLocations', 2) }}
|
{{ $t('allLocations', 2) }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-divider vertical />
|
<v-divider vertical />
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Replica of the API endpoint /cities
|
||||||
|
*/
|
||||||
export class CityModel {
|
export class CityModel {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
@@ -8,5 +11,6 @@ export class CityModel {
|
|||||||
name: string
|
name: string
|
||||||
address: string
|
address: string
|
||||||
image: string
|
image: string
|
||||||
|
nrOfConcerts: number
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Replica of the API endpoint /locations
|
||||||
|
*/
|
||||||
export class LocationModel {
|
export class LocationModel {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
@@ -7,4 +10,16 @@ export class LocationModel {
|
|||||||
name: string
|
name: string
|
||||||
country: string
|
country: string
|
||||||
}
|
}
|
||||||
|
concerts: Array<{
|
||||||
|
id: number
|
||||||
|
date: string
|
||||||
|
price: number
|
||||||
|
inStock: number
|
||||||
|
tour: {
|
||||||
|
name: string
|
||||||
|
offered: boolean
|
||||||
|
image: string
|
||||||
|
bandName: string
|
||||||
|
}
|
||||||
|
}>
|
||||||
}
|
}
|
||||||
@@ -53,6 +53,10 @@ export const useConcertStore = defineStore("concertStore", {
|
|||||||
await getAllGenres()
|
await getAllGenres()
|
||||||
.then(result => {
|
.then(result => {
|
||||||
this.genres = result.data
|
this.genres = result.data
|
||||||
|
|
||||||
|
this.genres.sort((a, b) => {
|
||||||
|
return a.name > b.name
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
await getAllCities()
|
await getAllCities()
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
{
|
{
|
||||||
"menu": {
|
"allEvents": "Alle Events",
|
||||||
"allConcerts": "Alle Konzerte",
|
|
||||||
"allLocations": "Alle Veranstaltungsorte",
|
"allLocations": "Alle Veranstaltungsorte",
|
||||||
"allBands": "Alle Bands"
|
"topEvents": "Top Events",
|
||||||
},
|
"topLocations": "Top Veranstaltungsorte",
|
||||||
"shows": {
|
|
||||||
"highlights": "Highlights",
|
|
||||||
"tickets": "Ticket | Tickets",
|
"tickets": "Ticket | Tickets",
|
||||||
"topConcerts": "Top Konzerte",
|
"concert": "Konzert | Konzerte",
|
||||||
"topBands": "Top Bands",
|
|
||||||
"topLocations": "Top Veranstaltungsorte"
|
|
||||||
},
|
|
||||||
"tours": {
|
|
||||||
"concert": "Konzert | Konzerte"
|
|
||||||
},
|
|
||||||
"preferences": {
|
"preferences": {
|
||||||
"pageSetup": "Seiteneinstellungen",
|
"pageSetup": "Seiteneinstellungen",
|
||||||
"selectedTheme": "Ausgewähltes Theme",
|
"selectedTheme": "Ausgewähltes Theme",
|
||||||
@@ -143,5 +134,10 @@
|
|||||||
"brand": "Marke | Marken",
|
"brand": "Marke | Marken",
|
||||||
"concerts": {
|
"concerts": {
|
||||||
"location": "Veranstaltungsort"
|
"location": "Veranstaltungsort"
|
||||||
}
|
},
|
||||||
|
"location": "Veranstaltungsort | Veranstaltungsorte",
|
||||||
|
"band": "Band | Bands",
|
||||||
|
"noEventsFound": "Keine Events gefunden",
|
||||||
|
"from": "ab",
|
||||||
|
"soldOut": "Ausverkauft"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
{
|
{
|
||||||
"menu": {
|
"allEvents": "All Events",
|
||||||
"allConcerts": "All Concerts",
|
|
||||||
"allLocations": "All Locations",
|
"allLocations": "All Locations",
|
||||||
"allBands": "All Bands"
|
"topEvents": "Top Events",
|
||||||
},
|
"topLocations": "Top Locations",
|
||||||
"shows": {
|
|
||||||
"highlights": "Highlights",
|
|
||||||
"tickets": "Ticket | Tickets",
|
"tickets": "Ticket | Tickets",
|
||||||
"topConcerts": "Top Concerts",
|
"concert": "Concert | Concerts",
|
||||||
"topBands": "Top Bands",
|
|
||||||
"topLocations": "Top Locations"
|
|
||||||
},
|
|
||||||
"tours": {
|
|
||||||
"concert": "Concert | Concerts"
|
|
||||||
},
|
|
||||||
"preferences": {
|
"preferences": {
|
||||||
"pageSetup": "Page setup",
|
"pageSetup": "Page setup",
|
||||||
"selectedTheme": "Selected theme",
|
"selectedTheme": "Selected theme",
|
||||||
@@ -143,5 +134,10 @@
|
|||||||
"brand": "Brand | Brands",
|
"brand": "Brand | Brands",
|
||||||
"concerts": {
|
"concerts": {
|
||||||
"location": "Locations"
|
"location": "Locations"
|
||||||
}
|
},
|
||||||
|
"location": "Location | Locations",
|
||||||
|
"band": "Band | Bands",
|
||||||
|
"noEventsFound": "No Events found",
|
||||||
|
"from": "from",
|
||||||
|
"soldOut": "Sold Out"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ const router = useRouter()
|
|||||||
append-icon="mdi-arrow-right"
|
append-icon="mdi-arrow-right"
|
||||||
class="mt-2"
|
class="mt-2"
|
||||||
color="primary"
|
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>
|
</outlined-button>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|||||||
@@ -15,20 +15,25 @@ const concertStore = useConcertStore()
|
|||||||
<highlight-carousel />
|
<highlight-carousel />
|
||||||
|
|
||||||
<v-container>
|
<v-container>
|
||||||
|
<v-row>
|
||||||
|
<v-spacer />
|
||||||
|
|
||||||
|
<v-col cols="10">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<section-divider :title="$t('shows.highlights')" />
|
<section-divider :title="$t('topEvents')" />
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="i in 6" cols="2">
|
<v-col v-for="i in 4" cols="3">
|
||||||
<card-with-top-image
|
<card-with-top-image
|
||||||
:image="'tours/' + concertStore.tours[i - 1].image"
|
:image="'tours/' + concertStore.tours[i - 1].image"
|
||||||
:title="concertStore.tours[i - 1].band.name"
|
:title="concertStore.tours[i - 1].band.name"
|
||||||
smaller-title
|
smaller-title
|
||||||
|
@click="router.push('/bands/' + concertStore.tours[i - 1].band.name.replaceAll(' ', '-').toLowerCase())"
|
||||||
>
|
>
|
||||||
Tickets ab {{ lowestTicketPrice(concertStore.tours[i - 1]) }} €
|
ab {{ lowestTicketPrice(concertStore.tours[i - 1]) }} €
|
||||||
</card-with-top-image>
|
</card-with-top-image>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -37,22 +42,22 @@ const concertStore = useConcertStore()
|
|||||||
<v-col>
|
<v-col>
|
||||||
<outlined-button
|
<outlined-button
|
||||||
append-icon="mdi-chevron-right"
|
append-icon="mdi-chevron-right"
|
||||||
@click="router.push('/shows/concerts')"
|
@click="router.push('/events')"
|
||||||
block
|
block
|
||||||
>
|
>
|
||||||
{{ $t('menu.allConcerts') }}
|
{{ $t('allEvents') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<section-divider :title="$t('shows.topLocations')" />
|
<section-divider :title="$t('topLocations')" />
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="i in 6" cols="2">
|
<v-col v-for="i in 8" cols="3">
|
||||||
<card-with-top-image
|
<card-with-top-image
|
||||||
:image="'locations/' + concertStore.locations[i + 2].image"
|
:image="'locations/' + concertStore.locations[i + 2].image"
|
||||||
:title="concertStore.locations[i + 2].name"
|
:title="concertStore.locations[i + 2].name"
|
||||||
@@ -67,12 +72,19 @@ const concertStore = useConcertStore()
|
|||||||
<v-col>
|
<v-col>
|
||||||
<outlined-button
|
<outlined-button
|
||||||
append-icon="mdi-chevron-right"
|
append-icon="mdi-chevron-right"
|
||||||
@click="router.push('/shows/locations')"
|
@click="router.push('/locations')"
|
||||||
block
|
block
|
||||||
>
|
>
|
||||||
{{ $t('menu.allLocations') }}
|
{{ $t('allLocations') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<v-spacer />
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
91
software/src/pages/locations/locationDetailPage/index.vue
Normal 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>
|
||||||
48
software/src/pages/locations/locationsPage/index.vue
Normal 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>
|
||||||
@@ -32,6 +32,15 @@ defineProps({
|
|||||||
|
|
||||||
<v-col>
|
<v-col>
|
||||||
<p class="text-h3">{{ band.name }}</p>
|
<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>
|
<p class="text-h6">{{ band.descriptionDe }}</p>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { dateStringToHumanReadableString } from '@/scripts/dateTimeScripts';
|
|||||||
import sectionDivider from '@/components/sectionDivider.vue';
|
import sectionDivider from '@/components/sectionDivider.vue';
|
||||||
import cardWithLeftImage from '@/components/cardWithLeftImage.vue';
|
import cardWithLeftImage from '@/components/cardWithLeftImage.vue';
|
||||||
import outlinedButton from '@/components/outlinedButton.vue';
|
import outlinedButton from '@/components/outlinedButton.vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
band: {
|
band: {
|
||||||
@@ -27,17 +30,26 @@ defineProps({
|
|||||||
:image="'http://localhost:3000/static/locations/' + concert.location.image"
|
:image="'http://localhost:3000/static/locations/' + concert.location.image"
|
||||||
:link="false"
|
:link="false"
|
||||||
>
|
>
|
||||||
<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>
|
||||||
|
|
||||||
|
<v-col>
|
||||||
|
<div class="text-h6">
|
||||||
{{ concert.location.name }}
|
{{ concert.location.name }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="text-h6">
|
||||||
{{ concert.location.address }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{{ concert.location.city.name }}
|
{{ concert.location.city.name }}
|
||||||
</div>
|
</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
<template #append>
|
<template #append>
|
||||||
<div class="pb-3">
|
<div class="pb-3">
|
||||||
@@ -46,9 +58,17 @@ defineProps({
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<outlined-button
|
<outlined-button
|
||||||
|
v-if="concert.inStock > 0"
|
||||||
prepend-icon="mdi-basket-plus"
|
prepend-icon="mdi-basket-plus"
|
||||||
>
|
>
|
||||||
Hinzufügen
|
{{ $t('add') }}
|
||||||
|
</outlined-button>
|
||||||
|
|
||||||
|
<outlined-button v-else
|
||||||
|
color="red"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
{{ $t('soldOut') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ const concertStore = useConcertStore()
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
>
|
>
|
||||||
<template #item="{ props, item }">
|
<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>
|
||||||
|
|
||||||
<template #selection="{ item }">
|
<template #selection="{ item }">
|
||||||
@@ -41,7 +44,7 @@ const concertStore = useConcertStore()
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
:items="concertStore.filteredLocations"
|
:items="concertStore.filteredLocations"
|
||||||
v-model="concertStore.locationFilter"
|
v-model="concertStore.locationFilter"
|
||||||
:label="$t('concerts.location')"
|
:label="$t('location', 2)"
|
||||||
density="compact"
|
density="compact"
|
||||||
:clearable="concertStore.locationFilter != null && concertStore.locationFilter.id != undefined"
|
:clearable="concertStore.locationFilter != null && concertStore.locationFilter.id != undefined"
|
||||||
:disabled="concertStore.cityFilter == null || concertStore.cityFilter.id == undefined"
|
:disabled="concertStore.cityFilter == null || concertStore.cityFilter.id == undefined"
|
||||||
@@ -50,7 +53,10 @@ const concertStore = useConcertStore()
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
>
|
>
|
||||||
<template #item="{ props, item }">
|
<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>
|
||||||
|
|
||||||
<template #selection="{ item }">
|
<template #selection="{ item }">
|
||||||
@@ -74,7 +80,9 @@ const concertStore = useConcertStore()
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
>
|
>
|
||||||
<template #item="{ props, item }">
|
<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>
|
||||||
|
|
||||||
<template #selection="{ item }">
|
<template #selection="{ item }">
|
||||||
@@ -29,12 +29,19 @@ const concertStore = useConcertStore()
|
|||||||
<card-with-left-image
|
<card-with-left-image
|
||||||
:title="tour.band.name + ' - ' + tour.name"
|
:title="tour.band.name + ' - ' + tour.name"
|
||||||
:image="'http://localhost:3000/static/tours/' + tour.image"
|
: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) }}
|
{{ 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>
|
<template #append>
|
||||||
|
<div>
|
||||||
|
<v-icon
|
||||||
|
icon="mdi-ticket"
|
||||||
|
color="secondary"
|
||||||
|
size="x-large"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
ab {{ lowestTicketPrice(tour) }} €
|
ab {{ lowestTicketPrice(tour) }} €
|
||||||
</template>
|
</template>
|
||||||
</card-with-left-image>
|
</card-with-left-image>
|
||||||
@@ -44,7 +51,7 @@ const concertStore = useConcertStore()
|
|||||||
<v-row v-else>
|
<v-row v-else>
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-empty-state
|
<v-empty-state
|
||||||
title="Keine Konzerte gefunden"
|
:title="$t('noEventsFound')"
|
||||||
icon="mdi-magnify"
|
icon="mdi-magnify"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
@@ -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>
|
|
||||||
@@ -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>
|
|
||||||
@@ -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>
|
|
||||||
@@ -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>
|
|
||||||
@@ -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>
|
|
||||||
@@ -2,13 +2,21 @@ import BasketPage from "@/pages/basketPage/index.vue"
|
|||||||
import HomePage from "@/pages/homePage/index.vue"
|
import HomePage from "@/pages/homePage/index.vue"
|
||||||
import { adminRoutes } from "./admin.routes";
|
import { adminRoutes } from "./admin.routes";
|
||||||
import { accountRoutes } from "./account.routes";
|
import { accountRoutes } from "./account.routes";
|
||||||
import { showRoutes } from "./show.routes";
|
|
||||||
import { systemRoutes } from "./system.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 = [
|
const routes = [
|
||||||
{ path: "/", component: HomePage },
|
{ path: "/", component: HomePage },
|
||||||
{ path: '/basket', component: BasketPage },
|
{ 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,
|
...accountRoutes,
|
||||||
...adminRoutes,
|
...adminRoutes,
|
||||||
...systemRoutes
|
...systemRoutes
|
||||||
|
|||||||
@@ -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 }
|
|
||||||
]
|
|
||||||