Display concerts with card views on "All concerts" page, adding image property for tours
@@ -239,6 +239,60 @@
|
|||||||
"bandId": 4
|
"bandId": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "Billy Talent",
|
||||||
|
"foundingYear": 1993,
|
||||||
|
"descriptionEn": "Billy Talent is a Canadian rock band from Mississauga, Ontario. They formed in 1993 with lead vocalist Benjamin Kowalewicz, guitarist Ian D'Sa, bassist Jonathan Gallant, and drummer Aaron Solowoniuk. There have been no lineup changes, although Solowoniuk has been on hiatus from the band since 2016 due to a relapse of multiple sclerosis. In the three decades since their inception, Billy Talent has sold well over a million physical albums in Canada alone and nearly 3 million albums internationally. During their most successful period, they were ranked as one of the top 10 best-selling native bands in Canada.",
|
||||||
|
"descriptionDe": "Billy Talent ist eine kanadische Rockband aus Mississauga, Ontario. Die Band spielte anfangs Punk, ordnet sich auf den späteren Alben jedoch eher im Alternative Rock ein. ",
|
||||||
|
"images": [ "billy-talent-1.jpg" ],
|
||||||
|
"logo": "billy-talent-logo.png",
|
||||||
|
"genreId": [ 2, 11, 12, 13 ],
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"name": "Benjamin Kowalewicz",
|
||||||
|
"bandId": 5,
|
||||||
|
"image": "benjamin-kowalewicz.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ian D'Sa ",
|
||||||
|
"bandId": 5,
|
||||||
|
"image": "ian-d-sa.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jonathan Gallant",
|
||||||
|
"bandId": 5,
|
||||||
|
"image": "jonathan-gallant.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jordan Hastings",
|
||||||
|
"bandId": 5,
|
||||||
|
"image": "jordan-hastings.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Josh Freese",
|
||||||
|
"bandId": 5,
|
||||||
|
"image": "josh-freese.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"accountId": 0,
|
||||||
|
"rating": 5,
|
||||||
|
"bandId": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accountId": 1,
|
||||||
|
"rating": 3,
|
||||||
|
"bandId": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accountId": 2,
|
||||||
|
"rating": 4,
|
||||||
|
"bandId": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"icon": "",
|
|
||||||
"name": "All"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"icon": "mdi-chip",
|
|
||||||
"name": "Electronic"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"icon": "mdi-soccer",
|
|
||||||
"name": "Sports"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"icon": "mdi-tshirt-crew",
|
|
||||||
"name": "Clothes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"icon": "mdi-bookshelf",
|
|
||||||
"name": "Books"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"icon": "mdi-guitar-electric",
|
|
||||||
"name": "Instruments"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 6,
|
|
||||||
"icon": "mdi-teddy-bear",
|
|
||||||
"name": "Toys"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -43,6 +43,18 @@
|
|||||||
{
|
{
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"name": "Post-Grunge"
|
"name": "Post-Grunge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "Punk-Rock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "Post-Hardcore"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "Pop-Punk"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2,40 +2,74 @@
|
|||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "Unlimited Love Tour",
|
"name": "Unlimited Love",
|
||||||
"bandId": 0,
|
"bandId": 0,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"shows": [
|
"image": "unlimited-love-tour.jpg",
|
||||||
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"date": "2024-12-03",
|
"date": "2024-10-18",
|
||||||
"price": 92,
|
"price": 92,
|
||||||
"inStock": 230,
|
"inStock": 930,
|
||||||
"locationId": 0,
|
"locationId": 0,
|
||||||
"tourId": 0
|
"tourId": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"date": "2024-10-23",
|
||||||
|
"price": 119.90,
|
||||||
|
"inStock": 8736,
|
||||||
|
"locationId": 4,
|
||||||
|
"tourId": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"date": "2024-10-26",
|
||||||
|
"price": 114.90,
|
||||||
|
"inStock": 2793,
|
||||||
|
"locationId": 8,
|
||||||
|
"tourId": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"date": "2024-11-02",
|
||||||
|
"price": 124.90,
|
||||||
|
"inStock": 3079,
|
||||||
|
"locationId": 12,
|
||||||
|
"tourId": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "The Bends Tour",
|
"name": "The Bends",
|
||||||
"bandId": 1,
|
"bandId": 1,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"shows": [
|
"image": "the-bends-tour.jpg",
|
||||||
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 4,
|
||||||
"date": "2024-11-30",
|
"date": "2024-11-30",
|
||||||
"price": 104,
|
"price": 108,
|
||||||
"inStock": 120,
|
"inStock": 1200,
|
||||||
"locationId": 2,
|
"locationId": 1,
|
||||||
"tourId": 1
|
"tourId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 5,
|
||||||
"date": "2024-12-01",
|
"date": "2024-12-01",
|
||||||
"price": 104,
|
"price": 104,
|
||||||
"inStock": 180,
|
"inStock": 1800,
|
||||||
"locationId": 0,
|
"locationId": 5,
|
||||||
|
"tourId": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"date": "2024-12-07",
|
||||||
|
"price": 99.90,
|
||||||
|
"inStock": 2438,
|
||||||
|
"locationId": 9,
|
||||||
"tourId": 1
|
"tourId": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -45,29 +79,55 @@
|
|||||||
"name": "European Tour",
|
"name": "European Tour",
|
||||||
"bandId": 2,
|
"bandId": 2,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"shows": [
|
"image": "european-tour-arctic-monkeys.jpg",
|
||||||
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 7,
|
||||||
"date": "2024-10-15",
|
"date": "2025-01-21",
|
||||||
"price": 80,
|
"price": 67.90,
|
||||||
"inStock": 99,
|
"inStock": 994,
|
||||||
"locationId": 3,
|
"locationId": 3,
|
||||||
"tourId": 2
|
"tourId": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"date": "2024-11-15",
|
||||||
|
"price": 79.90,
|
||||||
|
"inStock": 1073,
|
||||||
|
"locationId": 14,
|
||||||
|
"tourId": 2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "Moon Music Tour",
|
"name": "Music of the Spheres",
|
||||||
"bandId": 3,
|
"bandId": 3,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"shows": [
|
"image": "music-of-the-spheres.png",
|
||||||
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 9,
|
||||||
"date": "2024-10-15",
|
"date": "2024-12-07",
|
||||||
"price": 80,
|
"price": 124.90,
|
||||||
"inStock": 99,
|
"inStock": 765,
|
||||||
"locationId": 1,
|
"locationId": 13,
|
||||||
|
"tourId": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"date": "2025-01-17",
|
||||||
|
"price": 129.90,
|
||||||
|
"inStock": 989,
|
||||||
|
"locationId": 9,
|
||||||
|
"tourId": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"date": "2025-02-01",
|
||||||
|
"price": 134.90,
|
||||||
|
"inStock": 827,
|
||||||
|
"locationId": 4,
|
||||||
"tourId": 3
|
"tourId": 3
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -77,16 +137,42 @@
|
|||||||
"name": "But Here We Are Tour",
|
"name": "But Here We Are Tour",
|
||||||
"bandId": 4,
|
"bandId": 4,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"shows": [
|
"image": "but-here-we-are.jpg",
|
||||||
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 12,
|
||||||
"date": "2024-10-15",
|
"date": "2024-12-05",
|
||||||
"price": 80,
|
"price": 80,
|
||||||
"inStock": 99,
|
"inStock": 99,
|
||||||
"locationId": 2,
|
"locationId": 2,
|
||||||
"tourId": 4
|
"tourId": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "Crisis of Faith",
|
||||||
|
"bandId": 5,
|
||||||
|
"offered": true,
|
||||||
|
"image": "crisis-of-faith-tour.jpg",
|
||||||
|
"concerts": [
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"date": "2025-01-12",
|
||||||
|
"price": 81.90,
|
||||||
|
"inStock": 173,
|
||||||
|
"locationId": 2,
|
||||||
|
"tourId": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"date": "2025-02-01",
|
||||||
|
"price": 84.90,
|
||||||
|
"inStock": 192,
|
||||||
|
"locationId": 6,
|
||||||
|
"tourId": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ import { AccountRole } from "./models/user/accountRole.model"
|
|||||||
import { Genre } from "./models/acts/genre.model"
|
import { Genre } from "./models/acts/genre.model"
|
||||||
import { Location } from "./models/acts/location.model"
|
import { Location } from "./models/acts/location.model"
|
||||||
import { Band } from "./models/acts/band.model"
|
import { Band } from "./models/acts/band.model"
|
||||||
import { Show } from "./models/acts/show.model"
|
import { Concert } from "./models/acts/concert.model"
|
||||||
import { Member } from "./models/acts/member.model"
|
import { Member } from "./models/acts/member.model"
|
||||||
import { Rating } from "./models/acts/rating.model"
|
import { Rating } from "./models/acts/rating.model"
|
||||||
import { Tour } from "./models/acts/tour.model"
|
import { Tour } from "./models/acts/tour.model"
|
||||||
@@ -31,7 +31,7 @@ export const sequelize = new Sequelize({
|
|||||||
storage: "database.sqlite",
|
storage: "database.sqlite",
|
||||||
models: [
|
models: [
|
||||||
AccountRole, Account, Payment, Address,
|
AccountRole, Account, Payment, Address,
|
||||||
City, Location, Genre, Band, BandGenre, Rating, Member, Tour, Show,
|
City, Location, Genre, Band, BandGenre, Rating, Member, Tour, Concert,
|
||||||
Order, OrderItem
|
Order, OrderItem
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
BIN
software/backend/images/bands/billy-talent-1.jpg
Normal file
|
After Width: | Height: | Size: 395 KiB |
BIN
software/backend/images/tours/but-here-we-are.jpg
Normal file
|
After Width: | Height: | Size: 336 KiB |
BIN
software/backend/images/tours/crisis-of-faith-tour.jpg
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
software/backend/images/tours/european-tour-arctic-monkeys.jpg
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
software/backend/images/tours/music-of-the-spheres.png
Normal file
|
After Width: | Height: | Size: 492 KiB |
BIN
software/backend/images/tours/the-bends-tour.jpg
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
software/backend/images/tours/unlimited-love-tour.jpg
Normal file
|
After Width: | Height: | Size: 113 KiB |
@@ -4,7 +4,7 @@ import { Tour } from "./tour.model";
|
|||||||
import { OrderItem } from "../ordering/orderItem.model";
|
import { OrderItem } from "../ordering/orderItem.model";
|
||||||
|
|
||||||
@Table({ timestamps: false })
|
@Table({ timestamps: false })
|
||||||
export class Show extends Model {
|
export class Concert extends Model {
|
||||||
@Column
|
@Column
|
||||||
date: String
|
date: String
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BelongsTo, Column, ForeignKey, HasMany, Model, Table } from "sequelize-typescript";
|
import { BelongsTo, Column, ForeignKey, HasMany, Model, Table } from "sequelize-typescript";
|
||||||
import { Show } from "./show.model";
|
import { Concert } from "./concert.model";
|
||||||
import { City } from "./city.model";
|
import { City } from "./city.model";
|
||||||
|
|
||||||
@Table({ timestamps: false })
|
@Table({ timestamps: false })
|
||||||
@@ -20,8 +20,8 @@ export class Location extends Model {
|
|||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
|
|
||||||
@HasMany(() => Show)
|
@HasMany(() => Concert)
|
||||||
shows: Show[]
|
shows: Concert[]
|
||||||
|
|
||||||
@BelongsTo(() => City)
|
@BelongsTo(() => City)
|
||||||
city: City
|
city: City
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { BelongsTo, Column, ForeignKey, HasMany, Model, Table } from "sequelize-typescript";
|
import { BelongsTo, Column, ForeignKey, HasMany, Model, Table } from "sequelize-typescript";
|
||||||
import { Band } from "./band.model";
|
import { Band } from "./band.model";
|
||||||
import { Show } from "./show.model";
|
import { Concert } from "./concert.model";
|
||||||
|
|
||||||
@Table({ timestamps: false })
|
@Table({ timestamps: false })
|
||||||
export class Tour extends Model {
|
export class Tour extends Model {
|
||||||
@@ -13,12 +13,15 @@ export class Tour extends Model {
|
|||||||
@Column
|
@Column
|
||||||
offered: Boolean
|
offered: Boolean
|
||||||
|
|
||||||
|
@Column
|
||||||
|
image: String
|
||||||
|
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
|
|
||||||
@BelongsTo(() => Band)
|
@BelongsTo(() => Band)
|
||||||
band: Band
|
band: Band
|
||||||
|
|
||||||
@HasMany(() => Show)
|
@HasMany(() => Concert)
|
||||||
shows: Show[]
|
shows: Concert[]
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Model, BelongsTo, Column, ForeignKey, HasMany, HasOne, Table } from "sequelize-typescript";
|
import { Model, BelongsTo, Column, ForeignKey, HasMany, HasOne, Table } from "sequelize-typescript";
|
||||||
import { Show } from "../acts/show.model";
|
import { Concert } from "../acts/concert.model";
|
||||||
import { Order } from "./order.model";
|
import { Order } from "./order.model";
|
||||||
|
|
||||||
@Table({ timestamps: false })
|
@Table({ timestamps: false })
|
||||||
@@ -15,7 +15,7 @@ export class OrderItem extends Model {
|
|||||||
orderPrice: number
|
orderPrice: number
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
@ForeignKey(() => Show)
|
@ForeignKey(() => Concert)
|
||||||
showId: number
|
showId: number
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +23,6 @@ export class OrderItem extends Model {
|
|||||||
@BelongsTo(() => Order)
|
@BelongsTo(() => Order)
|
||||||
order: Order
|
order: Order
|
||||||
|
|
||||||
@BelongsTo(() => Show)
|
@BelongsTo(() => Concert)
|
||||||
product: Show
|
product: Concert
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Location } from "../models/acts/location.model";
|
import { Location } from "../models/acts/location.model";
|
||||||
import { Show } from "../models/acts/show.model";
|
import { Concert } from "../models/acts/concert.model";
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { Tour } from "../models/acts/tour.model";
|
import { Tour } from "../models/acts/tour.model";
|
||||||
import { City } from "../models/acts/city.model";
|
import { City } from "../models/acts/city.model";
|
||||||
|
|
||||||
export const show = Router()
|
export const concert = Router()
|
||||||
|
|
||||||
show.get("/:id", (req: Request, res: Response) => {
|
concert.get("/:id", (req: Request, res: Response) => {
|
||||||
Show.findByPk(req.params.id, {
|
Concert.findByPk(req.params.id, {
|
||||||
include: [
|
include: [
|
||||||
Tour,
|
Tour,
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Router, Request, Response } from "express";
|
import { Router, Request, Response } from "express";
|
||||||
import { Order } from "../models/ordering/order.model";
|
import { Order } from "../models/ordering/order.model";
|
||||||
import { Show } from "../models/acts/show.model";
|
import { Concert } from "../models/acts/concert.model";
|
||||||
import { OrderItem } from "../models/ordering/orderItem.model";
|
import { OrderItem } from "../models/ordering/orderItem.model";
|
||||||
import { Payment } from "../models/user/payment.model";
|
import { Payment } from "../models/user/payment.model";
|
||||||
import { Address } from "../models/user/address.model";
|
import { Address } from "../models/user/address.model";
|
||||||
@@ -18,7 +18,7 @@ order.get("/:id", (req: Request, res: Response) => {
|
|||||||
model: OrderItem,
|
model: OrderItem,
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: Show,
|
model: Concert,
|
||||||
include: [ Band, Location ],
|
include: [ Band, Location ],
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: [
|
exclude: [
|
||||||
@@ -50,7 +50,7 @@ order.post("/", (req: Request, res: Response) => {
|
|||||||
productId: orderItem.productId
|
productId: orderItem.productId
|
||||||
})
|
})
|
||||||
|
|
||||||
Show.decrement(
|
Concert.decrement(
|
||||||
"inStock",
|
"inStock",
|
||||||
{
|
{
|
||||||
by: orderItem.quantity,
|
by: orderItem.quantity,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Show } from "../models/acts/show.model";
|
import { Concert } from "../models/acts/concert.model";
|
||||||
import { Band } from "../models/acts/band.model";
|
import { Band } from "../models/acts/band.model";
|
||||||
import { Tour } from "../models/acts/tour.model";
|
import { Tour } from "../models/acts/tour.model";
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
@@ -19,7 +19,7 @@ tour.get("/", (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: Show,
|
model: Concert,
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: Location,
|
model: Location,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { Member } from '../models/acts/member.model'
|
|||||||
import { Genre } from '../models/acts/genre.model'
|
import { Genre } from '../models/acts/genre.model'
|
||||||
import { Band } from '../models/acts/band.model'
|
import { Band } from '../models/acts/band.model'
|
||||||
import { Location } from '../models/acts/location.model'
|
import { Location } from '../models/acts/location.model'
|
||||||
import { Show } from '../models/acts/show.model'
|
import { Concert } from '../models/acts/concert.model'
|
||||||
import { Tour } from '../models/acts/tour.model'
|
import { Tour } from '../models/acts/tour.model'
|
||||||
import { City } from '../models/acts/city.model'
|
import { City } from '../models/acts/city.model'
|
||||||
import { BandGenre } from '../models/acts/bandGenre.model'
|
import { BandGenre } from '../models/acts/bandGenre.model'
|
||||||
@@ -36,7 +36,7 @@ export function deleteAllTables() {
|
|||||||
Genre.destroy({ truncate: true })
|
Genre.destroy({ truncate: true })
|
||||||
Band.destroy({ truncate: true })
|
Band.destroy({ truncate: true })
|
||||||
Location.destroy({ truncate: true })
|
Location.destroy({ truncate: true })
|
||||||
Show.destroy({ truncate: true })
|
Concert.destroy({ truncate: true })
|
||||||
|
|
||||||
Address.destroy({ truncate: true })
|
Address.destroy({ truncate: true })
|
||||||
Payment.destroy({ truncate: true })
|
Payment.destroy({ truncate: true })
|
||||||
@@ -90,8 +90,8 @@ export async function prepopulateDatabase() {
|
|||||||
for (let tour of tours.data) {
|
for (let tour of tours.data) {
|
||||||
await Tour.create(tour)
|
await Tour.create(tour)
|
||||||
.then(async dataset => {
|
.then(async dataset => {
|
||||||
for (let show of tour.shows) {
|
for (let concert of tour.concerts) {
|
||||||
await Show.create(show)
|
await Concert.create(concert)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { api } from './routes/api.routes'
|
|||||||
import { startDatabase } from './database'
|
import { startDatabase } from './database'
|
||||||
import { order } from './routes/order.routes'
|
import { order } from './routes/order.routes'
|
||||||
import { account } from './routes/account.routes'
|
import { account } from './routes/account.routes'
|
||||||
import { show } from './routes/show.routes'
|
import { concert } from './routes/concert.routes'
|
||||||
import { band } from './routes/band.routes'
|
import { 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'
|
||||||
@@ -35,7 +35,7 @@ app.use('/static', express.static(path.join(__dirname, 'images')))
|
|||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
app.use("/api", api)
|
app.use("/api", api)
|
||||||
app.use("/shows", show)
|
app.use("/shows", concert)
|
||||||
app.use("/bands", band)
|
app.use("/bands", band)
|
||||||
app.use("/genres", genre)
|
app.use("/genres", genre)
|
||||||
app.use("/locations", location)
|
app.use("/locations", location)
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import navigationAppendItems from './components/navigation/navigationAppendItems
|
|||||||
import navigationPrependItems from './components/navigation/navigationPrependItems.vue';
|
import navigationPrependItems from './components/navigation/navigationPrependItems.vue';
|
||||||
import { usePreferencesStore } from './data/stores/preferencesStore';
|
import { usePreferencesStore } from './data/stores/preferencesStore';
|
||||||
import { useFeedbackStore } from './data/stores/feedbackStore';
|
import { useFeedbackStore } from './data/stores/feedbackStore';
|
||||||
import { useShowStore } from './data/stores/showStore';
|
import { useConcertStore } from './data/stores/concertStore';
|
||||||
|
|
||||||
const preferencesStore = usePreferencesStore()
|
const preferencesStore = usePreferencesStore()
|
||||||
const showStore = useShowStore()
|
const concertStore = useConcertStore()
|
||||||
const feedbackStore = useFeedbackStore()
|
const feedbackStore = useFeedbackStore()
|
||||||
|
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
|
||||||
theme.global.name.value = preferencesStore.theme
|
theme.global.name.value = preferencesStore.theme
|
||||||
|
|
||||||
showStore.fetchAllTours()
|
concertStore.fetchAllTours()
|
||||||
|
|
||||||
// Global watcher
|
// Global watcher
|
||||||
watch(() => preferencesStore.language, () => {
|
watch(() => preferencesStore.language, () => {
|
||||||
@@ -64,7 +64,7 @@ watch(() => preferencesStore.language, () => {
|
|||||||
</v-snackbar>
|
</v-snackbar>
|
||||||
|
|
||||||
<!-- Here changes the router the content -->
|
<!-- Here changes the router the content -->
|
||||||
<v-container max-width="1200" class="pt-0 pb-5">
|
<v-container max-width="1400" class="pt-0 pb-5">
|
||||||
<v-sheet color="sheet">
|
<v-sheet color="sheet">
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
|
|||||||
33
software/src/components/cardWithLeftImage.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps({
|
||||||
|
image: String,
|
||||||
|
title: String
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-card
|
||||||
|
variant="tonal"
|
||||||
|
link
|
||||||
|
>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="auto" class="pr-0">
|
||||||
|
<v-img :src="image" aspect-ratio="1" width="140" cover />
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<v-col class="pl-0">
|
||||||
|
<v-card-title v-if="title">
|
||||||
|
{{ title }}
|
||||||
|
</v-card-title>
|
||||||
|
|
||||||
|
<div class="px-4 pb-4" v-if="$slots.default">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<v-col cols="2" height="100%">
|
||||||
|
<slot name="append"></slot>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps({
|
defineProps({
|
||||||
image: String,
|
image: String,
|
||||||
title: String
|
title: String,
|
||||||
|
smallerTitle: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -17,10 +21,17 @@ defineProps({
|
|||||||
cover
|
cover
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-card-title v-if="title">
|
<div v-if="title">
|
||||||
|
<v-card-title v-if="!smallerTitle">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
|
|
||||||
|
<v-card-title v-else style="font-size: medium">
|
||||||
|
{{ title }}
|
||||||
|
</v-card-title>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="px-4 pb-4" v-if="$slots.default">
|
<div class="px-4 pb-4" v-if="$slots.default">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,12 +10,12 @@
|
|||||||
<v-divider vertical />
|
<v-divider vertical />
|
||||||
|
|
||||||
<v-btn
|
<v-btn
|
||||||
to="/shows/events"
|
to="/shows/concerts"
|
||||||
prepend-icon="mdi-ticket"
|
prepend-icon="mdi-ticket"
|
||||||
height="100%"
|
height="100%"
|
||||||
:rounded="false"
|
:rounded="false"
|
||||||
>
|
>
|
||||||
{{ $t('menu.allEvents', 2) }}
|
{{ $t('menu.allConcerts', 2) }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-divider vertical />
|
<v-divider vertical />
|
||||||
|
|||||||
7
software/src/data/api/concertApi.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import axios from "axios"
|
||||||
|
|
||||||
|
let BASE_URL = "http://localhost:3000/concerts"
|
||||||
|
|
||||||
|
export async function getAllConcerts() {
|
||||||
|
return await axios.get(BASE_URL)
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import axios from "axios"
|
|
||||||
|
|
||||||
let BASE_URL = "http://localhost:3000/shows"
|
|
||||||
|
|
||||||
export async function getAllShows() {
|
|
||||||
return await axios.get(BASE_URL)
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ export class BandModel {
|
|||||||
logo: string
|
logo: string
|
||||||
ratings: Array<RatingModel>
|
ratings: Array<RatingModel>
|
||||||
members: Array<MemberModel>
|
members: Array<MemberModel>
|
||||||
genre: {
|
genres: {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { ShowModel } from "./showModel"
|
import { ConcertModel } from "./concertModel"
|
||||||
|
|
||||||
export class BasketItemModel {
|
export class BasketItemModel {
|
||||||
id: number = -1
|
id: number = -1
|
||||||
quantity: number = 1
|
quantity: number = 1
|
||||||
product: ShowModel = new ShowModel()
|
concert: ConcertModel = new ConcertModel()
|
||||||
|
|
||||||
constructor(quantity: number, product: ShowModel) {
|
constructor(quantity: number, concert: ConcertModel) {
|
||||||
this.quantity = quantity
|
this.quantity = quantity
|
||||||
this.product = product
|
this.concert = concert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { LocationModel } from "./locationModel"
|
import { LocationModel } from "./locationModel"
|
||||||
|
|
||||||
export class ShowModel {
|
export class ConcertModel {
|
||||||
id: number
|
id: number
|
||||||
inStock: number
|
inStock: number
|
||||||
date: string
|
date: string
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ShowModel } from "./showModel"
|
import { ConcertModel } from "./concertModel"
|
||||||
|
|
||||||
export class OrderItemModel {
|
export class OrderItemModel {
|
||||||
orderId: number = -1
|
orderId: number = -1
|
||||||
quantity: number = 1
|
quantity: number = 1
|
||||||
orderPrice: number = 0
|
orderPrice: number = 0
|
||||||
product: ShowModel
|
product: ConcertModel
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { AccountModel } from "./accountModel"
|
|
||||||
import { BandModel } from "./bandModel"
|
import { BandModel } from "./bandModel"
|
||||||
|
|
||||||
export class RatingModel {
|
export class RatingModel {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { BandModel } from "./bandModel"
|
import { BandModel } from "./bandModel"
|
||||||
import { ShowModel } from "./showModel"
|
import { ConcertModel } from "./concertModel"
|
||||||
|
|
||||||
export class TourModel {
|
export class TourModel {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
offered: boolean
|
offered: boolean
|
||||||
band: BandModel
|
band: BandModel
|
||||||
shows: Array<ShowModel>
|
image: string
|
||||||
|
shows: Array<ConcertModel>
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import { useFeedbackStore } from "./feedbackStore";
|
|||||||
import { BannerStateEnum } from "../enums/bannerStateEnum";
|
import { BannerStateEnum } from "../enums/bannerStateEnum";
|
||||||
import { addOrder } from "../api/orderApi";
|
import { addOrder } from "../api/orderApi";
|
||||||
import { useAccountStore } from "./accountStore";
|
import { useAccountStore } from "./accountStore";
|
||||||
import { ShowModel } from "../models/showModel";
|
import { ConcertModel } from "../models/concertModel";
|
||||||
import { AddressModel } from "../models/addressModel";
|
import { AddressModel } from "../models/addressModel";
|
||||||
import { PaymentModel } from "../models/paymentModel";
|
import { PaymentModel } from "../models/paymentModel";
|
||||||
|
|
||||||
@@ -44,28 +44,28 @@ export const useBasketStore = defineStore('basketStore', {
|
|||||||
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTREMOVED)
|
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTREMOVED)
|
||||||
|
|
||||||
this.itemsInBasket = this.itemsInBasket.filter((basketItemModel: BasketItemModel) =>
|
this.itemsInBasket = this.itemsInBasket.filter((basketItemModel: BasketItemModel) =>
|
||||||
basketItemModel.product.id != item.product.id
|
basketItemModel.concert.id != item.concert.id
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an item to the basket. If the product is already in the basket, the quantity will increase
|
* Add an item to the basket. If the product is already in the basket, the quantity will increase
|
||||||
*
|
*
|
||||||
* @param show Show to add
|
* @param concert Concert to add
|
||||||
* @param quantity Quantity of the product
|
* @param quantity Quantity of the product
|
||||||
*/
|
*/
|
||||||
addItemToBasket(show: ShowModel, quantity: number) {
|
addItemToBasket(concert: ConcertModel, quantity: number) {
|
||||||
const feedbackStore = useFeedbackStore()
|
const feedbackStore = useFeedbackStore()
|
||||||
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTADDED)
|
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTADDED)
|
||||||
|
|
||||||
// Product is already in the basket, increase number of items
|
// Product is already in the basket, increase number of items
|
||||||
if (this.itemsInBasket.find((basketItem: BasketItemModel) =>
|
if (this.itemsInBasket.find((basketItem: BasketItemModel) =>
|
||||||
basketItem.product.id == show.id))
|
basketItem.concert.id == concert.id))
|
||||||
{
|
{
|
||||||
this.itemsInBasket.find((basketItem: BasketItemModel) =>
|
this.itemsInBasket.find((basketItem: BasketItemModel) =>
|
||||||
basketItem.product.id == show.id).quantity += quantity
|
basketItem.concert.id == concert.id).quantity += quantity
|
||||||
} else {
|
} else {
|
||||||
this.itemsInBasket.push(new BasketItemModel(quantity, show))
|
this.itemsInBasket.push(new BasketItemModel(quantity, concert))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import { getAllGenres } from "../api/genreApi";
|
|||||||
import { CityModel } from "../models/cityModel";
|
import { CityModel } from "../models/cityModel";
|
||||||
import { getAllCities } from "../api/cityApi";
|
import { getAllCities } from "../api/cityApi";
|
||||||
|
|
||||||
export const useShowStore = defineStore("showStore", {
|
export const useConcertStore = defineStore("concertStore", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
tours: useLocalStorage<Array<TourModel>>("hackmycart/showStore/tours", []),
|
tours: useLocalStorage<Array<TourModel>>("hackmycart/concertStore/tours", []),
|
||||||
bands: useLocalStorage<Array<BandModel>>("hackmycart/showStore/bands", []),
|
bands: useLocalStorage<Array<BandModel>>("hackmycart/concertStore/bands", []),
|
||||||
locations: useLocalStorage<Array<LocationModel>>("hackmycart/showStore/locations", []),
|
locations: useLocalStorage<Array<LocationModel>>("hackmycart/concertStore/locations", []),
|
||||||
cities: useLocalStorage<Array<CityModel>>("hackmycart/showStore/cities", []),
|
cities: useLocalStorage<Array<CityModel>>("hackmycart/concertStore/cities", []),
|
||||||
genres: useLocalStorage<Array<GenreModel>>("hackmycart/showStore/genres", [])
|
genres: useLocalStorage<Array<GenreModel>>("hackmycart/concertStore/genres", [])
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -25,6 +25,10 @@ export const useShowStore = defineStore("showStore", {
|
|||||||
await getAllTours()
|
await getAllTours()
|
||||||
.then(result => {
|
.then(result => {
|
||||||
this.tours = result.data
|
this.tours = result.data
|
||||||
|
|
||||||
|
this.tours.sort((a, b) => {
|
||||||
|
return new Date(a.shows[0].date) < new Date(b.shows[0].date) ? -1 : 1
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
await getAllBands()
|
await getAllBands()
|
||||||
@@ -5,7 +5,7 @@ import { LanguageEnum } from "../enums/languageEnum";
|
|||||||
|
|
||||||
export const usePreferencesStore = defineStore('preferencesStore', {
|
export const usePreferencesStore = defineStore('preferencesStore', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
theme: useLocalStorage<ThemeEnum>("hackmycart/preferencesStore/theme", ThemeEnum.DARKRED),
|
theme: useLocalStorage<ThemeEnum>("hackmycart/preferencesStore/theme", ThemeEnum.DARKBLUE),
|
||||||
language: useLocalStorage<LanguageEnum>("hackmycart/preferencesStore/language", LanguageEnum.GERMAN)
|
language: useLocalStorage<LanguageEnum>("hackmycart/preferencesStore/language", LanguageEnum.GERMAN)
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"menu": {
|
"menu": {
|
||||||
"allEvents": "Alle Events",
|
"allConcerts": "Alle Konzerte",
|
||||||
"allLocations": "Alle Veranstaltungsorte",
|
"allLocations": "Alle Veranstaltungsorte",
|
||||||
"allBands": "Alle Bands"
|
"allBands": "Alle Bands"
|
||||||
},
|
},
|
||||||
"shows": {
|
"shows": {
|
||||||
|
"highlights": "Highlights",
|
||||||
"tickets": "Ticket | Tickets",
|
"tickets": "Ticket | Tickets",
|
||||||
"topEvents": "Top Events",
|
"topConcerts": "Top Konzerte",
|
||||||
"topBands": "Top Bands",
|
"topBands": "Top Bands",
|
||||||
"topLocations": "Top Veranstaltungsorte"
|
"topLocations": "Top Veranstaltungsorte"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"menu": {
|
"menu": {
|
||||||
"allEvents": "All Events",
|
"allConcerts": "All Concerts",
|
||||||
"allLocations": "All Locations",
|
"allLocations": "All Locations",
|
||||||
"allBands": "All Bands"
|
"allBands": "All Bands"
|
||||||
},
|
},
|
||||||
"shows": {
|
"shows": {
|
||||||
|
"highlights": "Highlights",
|
||||||
"tickets": "Ticket | Tickets",
|
"tickets": "Ticket | Tickets",
|
||||||
"topEvents": "Top Events",
|
"topConcerts": "Top Concerts",
|
||||||
"topBands": "Top Bands",
|
"topBands": "Top Bands",
|
||||||
"topLocations": "Top Locations"
|
"topLocations": "Top Locations"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,18 +30,18 @@ function editQuantity(basketItem: BasketItemModel) {
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="basketItem in basketStore.itemsInBasket">
|
<tr v-for="basketItem in basketStore.itemsInBasket">
|
||||||
<!-- Category icon and name -->
|
<!-- Category icon and name -->
|
||||||
<td><v-icon :icon="basketItem.product.category.icon" />
|
<td><v-icon :icon="basketItem.concert.category.icon" />
|
||||||
{{ basketItem.product.category.name }}
|
{{ basketItem.concert.category.name }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Product brand -->
|
<!-- Product brand -->
|
||||||
<td>
|
<td>
|
||||||
{{ basketItem.product.brand.name }}
|
{{ basketItem.concert.brand.name }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Name of product -->
|
<!-- Name of product -->
|
||||||
<td>
|
<td>
|
||||||
{{ basketItem.product.name }}
|
{{ basketItem.concert.name }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Quantity -->
|
<!-- Quantity -->
|
||||||
@@ -73,18 +73,18 @@ function editQuantity(basketItem: BasketItemModel) {
|
|||||||
|
|
||||||
<!-- Total price -->
|
<!-- Total price -->
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<div v-if="basketItem.product.discount > 0">
|
<div v-if="basketItem.concert.discount > 0">
|
||||||
<strong class="font-weight-bold text-body-1 text-red-lighten-1">
|
<strong class="font-weight-bold text-body-1 text-red-lighten-1">
|
||||||
{{ calcPrice(basketItem.product.price, basketItem.product.discount, basketItem.quantity) }} €
|
{{ calcPrice(basketItem.concert.price, basketItem.concert.discount, basketItem.quantity) }} €
|
||||||
</strong>
|
</strong>
|
||||||
|
|
||||||
<div class="text-decoration-line-through ml-3 mt-1 text-caption">
|
<div class="text-decoration-line-through ml-3 mt-1 text-caption">
|
||||||
{{ calcPrice(basketItem.product.price, 0, basketItem.quantity) }} €
|
{{ calcPrice(basketItem.concert.price, 0, basketItem.quantity) }} €
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{{ calcPrice(basketItem.product.price, 0, basketItem.quantity) }} €
|
{{ calcPrice(basketItem.concert.price, 0, basketItem.quantity) }} €
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import OutlinedButton from '@/components/outlinedButton.vue';
|
import OutlinedButton from '@/components/outlinedButton.vue';
|
||||||
import { useShowStore } from '@/data/stores/showStore';
|
import { useConcertStore } from '@/data/stores/concertStore';
|
||||||
|
|
||||||
const tourStore = useShowStore()
|
const concertStore = useConcertStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -29,7 +29,7 @@ const tourStore = useShowStore()
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<v-carousel-item
|
<v-carousel-item
|
||||||
v-for="tour in tourStore.tours"
|
v-for="tour in concertStore.tours"
|
||||||
:src="'http://localhost:3000/static/bands/' + tour.band.images[0]"
|
:src="'http://localhost:3000/static/bands/' + tour.band.images[0]"
|
||||||
cover
|
cover
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useShowStore } from '@/data/stores/showStore';
|
import { useConcertStore } from '@/data/stores/concertStore';
|
||||||
import highlightCarousel from './highlightCarousel.vue';
|
import highlightCarousel from './highlightCarousel.vue';
|
||||||
import sectionDivider from '@/components/sectionDivider.vue';
|
import sectionDivider from '@/components/sectionDivider.vue';
|
||||||
import cardWithTopImage from '@/components/cardWithTopImage.vue';
|
import cardWithTopImage from '@/components/cardWithTopImage.vue';
|
||||||
import { calcRating } from '@/scripts/showsScripts';
|
import { calcRating, lowestTicketPrice } from '@/scripts/concertScripts';
|
||||||
import OutlinedButton from '@/components/outlinedButton.vue';
|
import OutlinedButton from '@/components/outlinedButton.vue';
|
||||||
import router from '@/plugins/router';
|
import router from '@/plugins/router';
|
||||||
|
|
||||||
const showStore = useShowStore()
|
const concertStore = useConcertStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -16,17 +16,18 @@ const showStore = useShowStore()
|
|||||||
<v-container>
|
<v-container>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<section-divider :title="$t('shows.topEvents')" />
|
<section-divider :title="$t('shows.highlights')" />
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="i in 4" cols="3">
|
<v-col v-for="i in 6" cols="2">
|
||||||
<card-with-top-image
|
<card-with-top-image
|
||||||
:image="'bands/' + showStore.tours[i].band.images[0]"
|
:image="'tours/' + concertStore.tours[i - 1].image"
|
||||||
:title="showStore.tours[i].name"
|
:title="concertStore.tours[i - 1].band.name"
|
||||||
|
smaller-title
|
||||||
>
|
>
|
||||||
{{ showStore.bands[i].name }}
|
Tickets ab {{ lowestTicketPrice(concertStore.tours[i - 1]) }} €
|
||||||
</card-with-top-image>
|
</card-with-top-image>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -35,10 +36,10 @@ const showStore = useShowStore()
|
|||||||
<v-col>
|
<v-col>
|
||||||
<outlined-button
|
<outlined-button
|
||||||
append-icon="mdi-chevron-right"
|
append-icon="mdi-chevron-right"
|
||||||
@click="router.push('/shows/events')"
|
@click="router.push('/shows/concerts')"
|
||||||
block
|
block
|
||||||
>
|
>
|
||||||
{{ $t('menu.allEvents') }}
|
{{ $t('menu.allConcerts') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -53,10 +54,10 @@ const showStore = useShowStore()
|
|||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="i in 4" cols="3">
|
<v-col v-for="i in 4" cols="3">
|
||||||
<card-with-top-image
|
<card-with-top-image
|
||||||
:image="'bands/' + showStore.bands[i - 1].logo"
|
:image="'bands/' + concertStore.bands[i - 1].logo"
|
||||||
:title="showStore.bands[i - 1].name"
|
:title="concertStore.bands[i - 1].name"
|
||||||
>
|
>
|
||||||
{{ showStore.bands[i - 1].genre.name }}
|
{{ concertStore.bands[i - 1].genres.name }}
|
||||||
|
|
||||||
<div class="d-flex justify-center pt-3">
|
<div class="d-flex justify-center pt-3">
|
||||||
<v-rating
|
<v-rating
|
||||||
@@ -65,7 +66,7 @@ const showStore = useShowStore()
|
|||||||
size="large"
|
size="large"
|
||||||
half-increments
|
half-increments
|
||||||
active-color="orange"
|
active-color="orange"
|
||||||
:model-value="calcRating(showStore.bands[i - 1].ratings)"
|
:model-value="calcRating(concertStore.bands[i - 1].ratings)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</card-with-top-image>
|
</card-with-top-image>
|
||||||
@@ -92,13 +93,13 @@ const showStore = useShowStore()
|
|||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="i in 4" cols="3">
|
<v-col v-for="i in 6" cols="2">
|
||||||
<card-with-top-image
|
<card-with-top-image
|
||||||
:image="'locations/' + showStore.locations[i + 2].image"
|
:image="'locations/' + concertStore.locations[i + 2].image"
|
||||||
:title="showStore.locations[i + 2].name"
|
:title="concertStore.locations[i + 2].name"
|
||||||
|
smaller-title
|
||||||
>
|
>
|
||||||
<div>{{ showStore.locations[i + 2].address }}</div>
|
{{ concertStore.locations[i + 2].city.name }}, {{ concertStore.locations[i + 2].city.country }}
|
||||||
{{ showStore.locations[i + 2].city.name }}, {{ showStore.locations[i + 2].city.country }}
|
|
||||||
</card-with-top-image>
|
</card-with-top-image>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -116,5 +117,3 @@ const showStore = useShowStore()
|
|||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import sectionDivider from '@/components/sectionDivider.vue';
|
import sectionDivider from '@/components/sectionDivider.vue';
|
||||||
import { useShowStore } from '@/data/stores/showStore';
|
import { useConcertStore } from '@/data/stores/concertStore';
|
||||||
import cardWithTopImage from '@/components/cardWithTopImage.vue';
|
import cardWithTopImage from '@/components/cardWithTopImage.vue';
|
||||||
import { calcRating } from '@/scripts/showsScripts';
|
import { calcRating } from '@/scripts/concertScripts';
|
||||||
|
|
||||||
const showStore = useShowStore()
|
const concertStore = useConcertStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-container>
|
<v-container>
|
||||||
<div v-for="genre in showStore.genres">
|
<div v-for="genre in concertStore.genres">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<section-divider
|
<section-divider
|
||||||
|
|||||||
37
software/src/pages/shows/concertsPage/index.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import cardWithLeftImage from '@/components/cardWithLeftImage.vue';
|
||||||
|
import { useConcertStore } from '@/data/stores/concertStore';
|
||||||
|
import { createDateRangeString, lowestTicketPrice } from '@/scripts/concertScripts';
|
||||||
|
|
||||||
|
const concertStore = useConcertStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-container>
|
||||||
|
<v-row>
|
||||||
|
<v-spacer />
|
||||||
|
|
||||||
|
<v-col cols="10">
|
||||||
|
<v-row v-for="tour of concertStore.tours">
|
||||||
|
<v-col>
|
||||||
|
<card-with-left-image
|
||||||
|
:title="tour.band.name + ' - ' + tour.name"
|
||||||
|
:image="'http://localhost:3000/static/tours/' + tour.image"
|
||||||
|
>
|
||||||
|
{{ createDateRangeString(tour) }}
|
||||||
|
<div>{{ tour.shows.length }} {{ $t('tours.concert', tour.shows.length) }}</div>
|
||||||
|
|
||||||
|
<template #append>
|
||||||
|
<div class="d-flex justify-center align-center text-h5" style="height: 100%;">
|
||||||
|
ab {{ lowestTicketPrice(tour) }} €
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</card-with-left-image>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<v-spacer />
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
Events
|
|
||||||
</template>
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import sectionDivider from '@/components/sectionDivider.vue';
|
import sectionDivider from '@/components/sectionDivider.vue';
|
||||||
import { useShowStore } from '@/data/stores/showStore';
|
import { useConcertStore } from '@/data/stores/concertStore';
|
||||||
import cardWithTopImage from '@/components/cardWithTopImage.vue';
|
import cardWithTopImage from '@/components/cardWithTopImage.vue';
|
||||||
|
|
||||||
const showStore = useShowStore()
|
const concertStore = useConcertStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-container>
|
<v-container>
|
||||||
<div v-for="city in showStore.cities">
|
<div v-for="city in concertStore.cities">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<section-divider
|
<section-divider
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import EventsPage from "@/pages/shows/eventsPage/index.vue";
|
import ConcertsPage from "@/pages/shows/concertsPage/index.vue";
|
||||||
import BandsPage from "@/pages/shows/bandsPage/index.vue";
|
import BandsPage from "@/pages/shows/bandsPage/index.vue";
|
||||||
import LocationsPage from "@/pages/shows/locationsPage/index.vue"
|
import LocationsPage from "@/pages/shows/locationsPage/index.vue"
|
||||||
import SearchPage from "@/pages/shows/searchPage/index.vue"
|
import SearchPage from "@/pages/shows/searchPage/index.vue"
|
||||||
|
|
||||||
export const showRoutes = [
|
export const showRoutes = [
|
||||||
{ path: '/shows/events', component: EventsPage },
|
{ path: '/shows/concerts', component: ConcertsPage },
|
||||||
{ path: '/shows/bands', component: BandsPage },
|
{ path: '/shows/bands', component: BandsPage },
|
||||||
{ path: '/shows/locations', component: LocationsPage },
|
{ path: '/shows/locations', component: LocationsPage },
|
||||||
{ path: '/shows/search', component: SearchPage },
|
{ path: '/shows/search', component: SearchPage },
|
||||||
|
|||||||
65
software/src/scripts/concertScripts.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { RatingModel } from "@/data/models/ratingModel"
|
||||||
|
import { dateToHumanReadableString } from "./dateTimeScripts"
|
||||||
|
import { TourModel } from "@/data/models/tourModel"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a price based on parameters
|
||||||
|
*
|
||||||
|
* @param price Original price for one unit
|
||||||
|
* @param discount Discount in percent
|
||||||
|
* @param quantity Number of units
|
||||||
|
*
|
||||||
|
* @returns Price rounded to two digits
|
||||||
|
*/
|
||||||
|
export function calcPrice(price: number, discount: number = 0, quantity: number = 1): number {
|
||||||
|
return Math.round(quantity * price * ((100 - discount) / 100) * 100) / 100
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the average of an Array of ratings
|
||||||
|
*
|
||||||
|
* @param ratings Array of ratings
|
||||||
|
*
|
||||||
|
* @returns Average rating as number
|
||||||
|
*/
|
||||||
|
export function calcRating(ratings: Array<RatingModel>) {
|
||||||
|
let sum = 0
|
||||||
|
|
||||||
|
for (let rating of ratings) {
|
||||||
|
sum += rating.rating
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum / ratings.length
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDateRangeString(tour: TourModel) {
|
||||||
|
const dateArray = []
|
||||||
|
|
||||||
|
for (let concert of tour.shows) {
|
||||||
|
dateArray.push(new Date(concert.date))
|
||||||
|
}
|
||||||
|
|
||||||
|
dateArray.sort(function (a, b) {
|
||||||
|
return a - b
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if (dateArray.length > 1) {
|
||||||
|
return dateToHumanReadableString(dateArray[0]) + ' - ' +
|
||||||
|
dateToHumanReadableString(dateArray[dateArray.length - 1])
|
||||||
|
} else {
|
||||||
|
return dateToHumanReadableString(dateArray[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function lowestTicketPrice(tour: TourModel): string {
|
||||||
|
const priceArray : Array<number> = []
|
||||||
|
|
||||||
|
for (let concert of tour.shows) {
|
||||||
|
priceArray.push(concert.price)
|
||||||
|
}
|
||||||
|
|
||||||
|
priceArray.sort()
|
||||||
|
|
||||||
|
return priceArray[0].toFixed(2)
|
||||||
|
}
|
||||||
5
software/src/scripts/dateTimeScripts.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export function dateToHumanReadableString(date: Date) {
|
||||||
|
return String(date.getDate()).padStart(2, '0') + '.' +
|
||||||
|
String(date.getMonth() + 1).padStart(2, '0') + '.' +
|
||||||
|
date.getFullYear()
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { RatingModel } from "@/data/models/ratingModel"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate a price based on parameters
|
|
||||||
*
|
|
||||||
* @param price Original price for one unit
|
|
||||||
* @param discount Discount in percent
|
|
||||||
* @param quantity Number of units
|
|
||||||
*
|
|
||||||
* @returns Price rounded to two digits
|
|
||||||
*/
|
|
||||||
export function calcPrice(price: number, discount: number = 0, quantity: number = 1): number {
|
|
||||||
return Math.round(quantity * price * ((100 - discount) / 100) * 100) / 100
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the average of an Array of ratings
|
|
||||||
*
|
|
||||||
* @param ratings Array of ratings
|
|
||||||
*
|
|
||||||
* @returns Average rating as number
|
|
||||||
*/
|
|
||||||
export function calcRating(ratings: Array<RatingModel>) {
|
|
||||||
let sum = 0
|
|
||||||
|
|
||||||
for (let rating of ratings) {
|
|
||||||
sum += rating.rating
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum / ratings.length
|
|
||||||
}
|
|
||||||