Location page displays city groups with all available concert locations
@@ -3,7 +3,153 @@
|
|||||||
{
|
{
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "Hannover",
|
"name": "Hannover",
|
||||||
"country": "Germany"
|
"country": "Germany",
|
||||||
|
"image": "hannover.jpg",
|
||||||
|
"locations": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Swiss Life Hall",
|
||||||
|
"address": "Ferdinand-Wilhelm-Fricke-Weg 8",
|
||||||
|
"cityId": 0,
|
||||||
|
"image": "swiss-life-hall.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Capitol",
|
||||||
|
"address": "Schwarzer Bär 2",
|
||||||
|
"cityId": 0,
|
||||||
|
"image": "capitol.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "ZAG Arena",
|
||||||
|
"address": "EXPO-Plaza 7",
|
||||||
|
"cityId": 0,
|
||||||
|
"image": "zag-arena.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Kulturzentrum Faust",
|
||||||
|
"address": "Zur Bettfedernfabrik 3",
|
||||||
|
"cityId": 0,
|
||||||
|
"image": "faust-hannover.jpg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "München",
|
||||||
|
"country": "Germany",
|
||||||
|
"image": "munich.jpg",
|
||||||
|
"locations": [
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "Olympiahalle München",
|
||||||
|
"address": "Spiridon-Louis-Ring 21",
|
||||||
|
"cityId": 1,
|
||||||
|
"image": "olympiahalle-munich.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "Schlachthof München",
|
||||||
|
"address": "Zenettistraße 9",
|
||||||
|
"cityId": 1,
|
||||||
|
"image": "schlachthof-munich.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "Muffatwerk",
|
||||||
|
"address": "Zellstraße 4",
|
||||||
|
"cityId": 1,
|
||||||
|
"image": "muffatwerk.jpg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Hamburg",
|
||||||
|
"country": "Germany",
|
||||||
|
"image": "hamburg.jpg",
|
||||||
|
"locations": [
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "Elbphilharmonie Hamburg",
|
||||||
|
"address": "Platz der deutschen Einheit",
|
||||||
|
"cityId": 2,
|
||||||
|
"image": "elbphilharmonie-hamburg.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "Volksparkstadion",
|
||||||
|
"address": "Sylvesterallee 7",
|
||||||
|
"cityId": 2,
|
||||||
|
"image": "volksparkstadion-hamburg.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "Barclays Arena",
|
||||||
|
"address": "Sylvesterallee 10",
|
||||||
|
"cityId": 2,
|
||||||
|
"image": "barclays-arena.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "Stage Theater im Hafen Hamburg",
|
||||||
|
"address": "Norderelbestraße 6",
|
||||||
|
"cityId": 2,
|
||||||
|
"image": "stage-theater-hamburg.jpg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Berlin",
|
||||||
|
"country": "Germany",
|
||||||
|
"image": "berlin.jpg",
|
||||||
|
"locations": [
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "Waldbühne Berlin",
|
||||||
|
"address": "Am Glockenturm",
|
||||||
|
"cityId": 3,
|
||||||
|
"image": "waldbuehne-berlin.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "Olympiastadion Berlin",
|
||||||
|
"address": "Olympischer Platz 3",
|
||||||
|
"cityId": 3,
|
||||||
|
"image": "olympiastadion-berlin.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "Uber Arena Berlin",
|
||||||
|
"address": "Uber-Platz 1",
|
||||||
|
"cityId": 3,
|
||||||
|
"image": "uber-arena-berlin.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"name": "Columbiahalle",
|
||||||
|
"address": "Columbiadamm 13-21",
|
||||||
|
"cityId": 3,
|
||||||
|
"image": "columbiahalle.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"name": "Astra Kulturhaus",
|
||||||
|
"address": "Revaler Straße 99",
|
||||||
|
"cityId": 3,
|
||||||
|
"image": "astra-kulturhaus.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"name": "Deutsche Oper Berlin",
|
||||||
|
"address": "Bismarckstraße 35",
|
||||||
|
"cityId": 3,
|
||||||
|
"image": "deutsche-oper-berlin.jpg"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "Alternatve Rock"
|
"name": "Alternative Rock"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"name": "Swiss Life Hall",
|
|
||||||
"address": "Ferdinand-Wilhlem-Fricke-Weg 8",
|
|
||||||
"cityId": 0,
|
|
||||||
"image": "swiss-life-hall.jpg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "Capitol",
|
|
||||||
"address": "Schwarzer Bär 2",
|
|
||||||
"cityId": 0,
|
|
||||||
"image": "capitol.jpg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "ZAG Arena",
|
|
||||||
"address": "EXPO-Plaza 7",
|
|
||||||
"cityId": 0,
|
|
||||||
"image": "zag-arena.jpg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Kulturzentrum Faust",
|
|
||||||
"address": "Zur Bettfedernfabrik 3",
|
|
||||||
"cityId": 0,
|
|
||||||
"image": "faust-hannover.jpg"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
BIN
software/backend/images/cities/berlin.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
software/backend/images/cities/hamburg.jpg
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
software/backend/images/cities/hannover.jpg
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
software/backend/images/cities/munich.jpg
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
software/backend/images/locations/astra-kulturhaus.jpg
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
software/backend/images/locations/barclays-arena.jpg
Normal file
|
After Width: | Height: | Size: 383 KiB |
BIN
software/backend/images/locations/columbiahalle.jpg
Normal file
|
After Width: | Height: | Size: 624 KiB |
BIN
software/backend/images/locations/deutsche-oper-berlin.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
software/backend/images/locations/elbphilharmonie-hamburg.jpg
Normal file
|
After Width: | Height: | Size: 386 KiB |
BIN
software/backend/images/locations/muffatwerk.jpg
Normal file
|
After Width: | Height: | Size: 329 KiB |
BIN
software/backend/images/locations/olympiahalle-munich.jpg
Normal file
|
After Width: | Height: | Size: 951 KiB |
BIN
software/backend/images/locations/olympiastadion-berlin.jpg
Normal file
|
After Width: | Height: | Size: 600 KiB |
BIN
software/backend/images/locations/schlachthof-munich.jpg
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
software/backend/images/locations/stage-theater-hamburg.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
software/backend/images/locations/uber-arena-berlin.jpg
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
software/backend/images/locations/volksparkstadion-hamburg.jpg
Normal file
|
After Width: | Height: | Size: 423 KiB |
BIN
software/backend/images/locations/waldbuehne-berlin.jpg
Normal file
|
After Width: | Height: | Size: 321 KiB |
@@ -9,6 +9,9 @@ export class City extends Model {
|
|||||||
@Column
|
@Column
|
||||||
country: String
|
country: String
|
||||||
|
|
||||||
|
@Column
|
||||||
|
image: String
|
||||||
|
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
|
|
||||||
|
|||||||
14
software/backend/routes/city.routes.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Location } from "../models/acts/location.model";
|
||||||
|
import { City } from "../models/acts/city.model";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
|
export const city = Router()
|
||||||
|
|
||||||
|
city.get("/", (req: Request, res: Response) => {
|
||||||
|
City.findAll({
|
||||||
|
include: [ Location ]
|
||||||
|
})
|
||||||
|
.then(cities => {
|
||||||
|
res.status(200).json(cities)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -19,7 +19,6 @@ import orderItems from "./../data/orderItems.json"
|
|||||||
import accountRoles from "./../data/accountRoles.json"
|
import accountRoles from "./../data/accountRoles.json"
|
||||||
import bands from "./../data/bands.json"
|
import bands from "./../data/bands.json"
|
||||||
import genres from "./../data/genres.json"
|
import genres from "./../data/genres.json"
|
||||||
import locations from "./../data/locations.json"
|
|
||||||
import tours from "./../data/tours.json"
|
import tours from "./../data/tours.json"
|
||||||
import cities from "./../data/cities.json"
|
import cities from "./../data/cities.json"
|
||||||
|
|
||||||
@@ -50,8 +49,14 @@ export function deleteAllTables() {
|
|||||||
export async function prepopulateDatabase() {
|
export async function prepopulateDatabase() {
|
||||||
AccountRole.bulkCreate(accountRoles.data)
|
AccountRole.bulkCreate(accountRoles.data)
|
||||||
Genre.bulkCreate(genres.data)
|
Genre.bulkCreate(genres.data)
|
||||||
City.bulkCreate(cities.data)
|
|
||||||
Location.bulkCreate(locations.data)
|
for (let city of cities.data) {
|
||||||
|
await City.create(city)
|
||||||
|
.then(dataset => {
|
||||||
|
Location.bulkCreate(city.locations)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Account & Sub tables
|
// Account & Sub tables
|
||||||
for (let account of accounts.data) {
|
for (let account of accounts.data) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { band } from './routes/band.routes'
|
|||||||
import { genre } from './routes/genre.routes'
|
import { genre } from './routes/genre.routes'
|
||||||
import { location } from './routes/location.routes'
|
import { location } from './routes/location.routes'
|
||||||
import { tour } from './routes/tour.routes'
|
import { tour } from './routes/tour.routes'
|
||||||
|
import { city } from './routes/city.routes'
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const port = 3000
|
const port = 3000
|
||||||
@@ -41,6 +42,7 @@ app.use("/locations", location)
|
|||||||
app.use("/orders", order)
|
app.use("/orders", order)
|
||||||
app.use("/accounts", account)
|
app.use("/accounts", account)
|
||||||
app.use("/tours", tour)
|
app.use("/tours", tour)
|
||||||
|
app.use("/cities", city)
|
||||||
|
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ defineProps({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-card>
|
<v-card
|
||||||
|
variant="outlined"
|
||||||
|
link
|
||||||
|
>
|
||||||
<v-img
|
<v-img
|
||||||
:src="'http://localhost:3000/static/' + image"
|
:src="'http://localhost:3000/static/' + image"
|
||||||
aspect-ratio="1"
|
aspect-ratio="1"
|
||||||
|
|||||||
@@ -1,11 +1,44 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps({
|
defineProps({
|
||||||
title: String
|
title: String,
|
||||||
|
image: String
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function backgroundStyle(image: string) {
|
||||||
|
return {
|
||||||
|
"background-image": 'http://localhost:3000/static/cities/hannover.jpg',
|
||||||
|
height: `100px`
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-row class="pt-3">
|
<div v-if="image" class="pt-1">
|
||||||
|
<v-img
|
||||||
|
:src="'http://localhost:3000/static/' + image"
|
||||||
|
height="120"
|
||||||
|
gradient="to top right, rgba(0,0,0,.5), rgba(0,0,0,.7)"
|
||||||
|
cover
|
||||||
|
class="rounded-t-xl"
|
||||||
|
>
|
||||||
|
<v-container
|
||||||
|
height="100%"
|
||||||
|
class="d-flex justify-center align-center"
|
||||||
|
>
|
||||||
|
<v-row>
|
||||||
|
<v-spacer />
|
||||||
|
|
||||||
|
<v-col class="v-col-auto">
|
||||||
|
<span class="text-h3" style="color: white;">{{ title }}</span>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<v-spacer />
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</v-img>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-row v-else class="pt-3">
|
||||||
<v-col class="d-flex justify-center align-center">
|
<v-col class="d-flex justify-center align-center">
|
||||||
<v-sheet height="12" width="100%" color="primary" :rounded="true" />
|
<v-sheet height="12" width="100%" color="primary" :rounded="true" />
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|||||||
7
software/src/data/api/cityApi.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import axios from "axios"
|
||||||
|
|
||||||
|
const BASE_URL = "http://localhost:3000/cities"
|
||||||
|
|
||||||
|
export async function getAllCities() {
|
||||||
|
return await axios.get(BASE_URL)
|
||||||
|
}
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
export class CityModel {
|
export class CityModel {
|
||||||
id: Number
|
id: number
|
||||||
name: String
|
name: string
|
||||||
country: String
|
country: string
|
||||||
|
image: string
|
||||||
|
locations: Array<{
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
address: string
|
||||||
|
image: string
|
||||||
|
}>
|
||||||
}
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export class FilterModel {
|
|
||||||
icon: string
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export class GenreModel {
|
export class GenreModel {
|
||||||
id: Number
|
id: number
|
||||||
name: String
|
name: string
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { CityModel } from "./cityModel"
|
|
||||||
|
|
||||||
export class LocationModel {
|
export class LocationModel {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
address: string
|
address: string
|
||||||
city: CityModel
|
|
||||||
image: string
|
image: string
|
||||||
|
city: {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
country: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,6 @@ import { BandModel } from "./bandModel"
|
|||||||
|
|
||||||
export class MemberModel {
|
export class MemberModel {
|
||||||
id: Number
|
id: Number
|
||||||
name: String
|
name: string
|
||||||
image: String
|
image: string
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { LocationModel } from "./locationModel"
|
import { LocationModel } from "./locationModel"
|
||||||
|
|
||||||
export class ShowModel {
|
export class ShowModel {
|
||||||
id: Number
|
id: number
|
||||||
inStock: Number
|
inStock: number
|
||||||
date: String
|
date: string
|
||||||
price: Number
|
price: number
|
||||||
location: LocationModel
|
location: LocationModel
|
||||||
}
|
}
|
||||||
@@ -7,12 +7,17 @@ import { getAllBands } from "../api/bandApi";
|
|||||||
import { BandModel } from "../models/bandModel";
|
import { BandModel } from "../models/bandModel";
|
||||||
import { LocationModel } from "../models/locationModel";
|
import { LocationModel } from "../models/locationModel";
|
||||||
import { getAllLocations } from "../api/locationApi";
|
import { getAllLocations } from "../api/locationApi";
|
||||||
|
import { getAllGenres } from "../api/genreApi";
|
||||||
|
import { CityModel } from "../models/cityModel";
|
||||||
|
import { getAllCities } from "../api/cityApi";
|
||||||
|
|
||||||
export const useShowStore = defineStore("showStore", {
|
export const useShowStore = defineStore("showStore", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
tours: useLocalStorage<Array<TourModel>>("hackmycart/showStore/tours", []),
|
tours: useLocalStorage<Array<TourModel>>("hackmycart/showStore/tours", []),
|
||||||
bands: useLocalStorage<Array<BandModel>>("hackmycart/showStore/bands", []),
|
bands: useLocalStorage<Array<BandModel>>("hackmycart/showStore/bands", []),
|
||||||
locations: useLocalStorage<Array<LocationModel>>("hackmycart/showStore/locations", [])
|
locations: useLocalStorage<Array<LocationModel>>("hackmycart/showStore/locations", []),
|
||||||
|
cities: useLocalStorage<Array<CityModel>>("hackmycart/showStore/cities", []),
|
||||||
|
genres: useLocalStorage<Array<GenreModel>>("hackmycart/showStore/genres", [])
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -31,6 +36,16 @@ export const useShowStore = defineStore("showStore", {
|
|||||||
.then(result => {
|
.then(result => {
|
||||||
this.locations = result.data
|
this.locations = result.data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await getAllGenres()
|
||||||
|
.then(result => {
|
||||||
|
this.genres = result.data
|
||||||
|
})
|
||||||
|
|
||||||
|
await getAllCities()
|
||||||
|
.then(result => {
|
||||||
|
this.cities = result.data
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1,6 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import sectionDivider from '@/components/sectionDivider.vue';
|
||||||
|
import { useShowStore } from '@/data/stores/showStore';
|
||||||
|
|
||||||
|
const showStore = useShowStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
Genres
|
<v-container>
|
||||||
|
<div v-for="genre in showStore.genres">
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<section-divider :title="genre.name" />
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</div>
|
||||||
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,6 +1,33 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import sectionDivider from '@/components/sectionDivider.vue';
|
||||||
|
import { useShowStore } from '@/data/stores/showStore';
|
||||||
|
import cardWithTopImage from '@/components/cardWithTopImage.vue';
|
||||||
|
|
||||||
|
const showStore = useShowStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
Locations
|
<v-container>
|
||||||
|
<div v-for="city in showStore.cities">
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<section-divider
|
||||||
|
:title="city.name"
|
||||||
|
:image="'cities/' + city.image"
|
||||||
|
/>
|
||||||
|
</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>
|
</template>
|
||||||