More skelton loader, add optional parameters to /locations and /events
30
README.md
@@ -53,7 +53,7 @@ The application host it's data in a SQLite database. The access is managed by an
|
||||
#### Listing existing
|
||||
|
||||
<details>
|
||||
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/events?city=cityName&genre=genreName</b></code> <code> (Get all events, filtered by city and genre)</code>
|
||||
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/events?city=cityName&genre=genreName&count=nrOfItems&sort=sortDirection</b></code> <code> (Get all events, filtered by city and genre)</code>
|
||||
</summary>
|
||||
|
||||
##### Parameters
|
||||
@@ -61,16 +61,31 @@ The application host it's data in a SQLite database. The access is managed by an
|
||||
> | :---: | --- | --- | --- |
|
||||
> | `cityName` | optional | string | Name of the city to filter for |
|
||||
> | `genreName` | optional | string | Name of the genre to filter for |
|
||||
> | `nrOfItems` | optional | number | Limits number of results |
|
||||
> | `sortDirection` | optional | string | Sort by number of concerts, 'asc' or 'desc' |
|
||||
|
||||
##### Responses
|
||||
> | http code | content-type | response |
|
||||
> | :---: | --- | --- |
|
||||
> | `200` | `application/json` | `Event` + `Array<Concert + Location>` + `Array<Band>` |
|
||||
> | `200` | `application/json` | `Array<Event + Array<Concert + Location + City> + Band & Genre>` |
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/locations?count=nrOfItems&sort=sortDirection</b></code> <code> (Get all locations)</code>
|
||||
</summary>
|
||||
|
||||
##### Parameters
|
||||
> | name | type | data type | description |
|
||||
> | :---: | --- | --- | --- |
|
||||
> | `nrOfItems` | optional | number | Limits number of results |
|
||||
> | `sortDirection` | optional | string | Sort by number of concerts, 'asc' or 'desc' |
|
||||
|
||||
##### Responses
|
||||
> | http code | content-type | response |
|
||||
> | :---: | --- | --- |
|
||||
> | `200` | `application/json` | `Array<Location + City + Array<Concert + Event>>` |
|
||||
</details>
|
||||
|
||||
|
||||
Down here: todo!
|
||||
@@ -156,18 +171,7 @@ Down here: todo!
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/locations</b></code> <code> (Get all locations)</code>
|
||||
</summary>
|
||||
|
||||
##### Parameters
|
||||
> None
|
||||
|
||||
##### Responses
|
||||
> | http code | content-type | response |
|
||||
> | :---: | --- | --- |
|
||||
> | `200` | `application/json` | `Array<Location>` + `City` |
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "Unlimited Love",
|
||||
"bandId": 0,
|
||||
"offered": true,
|
||||
"image": "unlimited-love-tour.jpg",
|
||||
"image": "events/unlimited-love-tour.jpg",
|
||||
"concerts": [
|
||||
{
|
||||
"id": 0,
|
||||
@@ -49,7 +49,7 @@
|
||||
"name": "The Bends",
|
||||
"bandId": 1,
|
||||
"offered": true,
|
||||
"image": "the-bends-tour.jpg",
|
||||
"image": "events/the-bends-tour.jpg",
|
||||
"concerts": [
|
||||
{
|
||||
"id": 5,
|
||||
@@ -79,7 +79,7 @@
|
||||
"name": "European Tour",
|
||||
"bandId": 2,
|
||||
"offered": true,
|
||||
"image": "european-tour-arctic-monkeys.jpg",
|
||||
"image": "events/european-tour-arctic-monkeys.jpg",
|
||||
"concerts": [
|
||||
{
|
||||
"id": 8,
|
||||
@@ -102,7 +102,7 @@
|
||||
"name": "Music of the Spheres",
|
||||
"bandId": 3,
|
||||
"offered": true,
|
||||
"image": "music-of-the-spheres.png",
|
||||
"image": "events/music-of-the-spheres.png",
|
||||
"concerts": [
|
||||
{
|
||||
"id": 10,
|
||||
@@ -132,7 +132,7 @@
|
||||
"name": "But Here We Are Tour",
|
||||
"bandId": 4,
|
||||
"offered": true,
|
||||
"image": "but-here-we-are.jpg",
|
||||
"image": "events/but-here-we-are.jpg",
|
||||
"concerts": [
|
||||
{
|
||||
"id": 13,
|
||||
@@ -148,7 +148,7 @@
|
||||
"name": "Crisis of Faith",
|
||||
"bandId": 5,
|
||||
"offered": true,
|
||||
"image": "crisis-of-faith-tour.jpg",
|
||||
"image": "events/crisis-of-faith-tour.jpg",
|
||||
"concerts": [
|
||||
{
|
||||
"id": 14,
|
||||
@@ -171,7 +171,7 @@
|
||||
"name": "Back to the Water Below",
|
||||
"bandId": 6,
|
||||
"offered": true,
|
||||
"image": "back-to-the-water-below.jpg",
|
||||
"image": "events/back-to-the-water-below.jpg",
|
||||
"concerts": [
|
||||
{
|
||||
"id": 16,
|
||||
@@ -194,7 +194,7 @@
|
||||
"name": "Will of the People Tour",
|
||||
"bandId": 7,
|
||||
"offered": true,
|
||||
"image": "will-of-the-people-tour.jpg",
|
||||
"image": "events/will-of-the-people-tour.jpg",
|
||||
"concerts": [
|
||||
{
|
||||
"id": 18,
|
||||
|
||||
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 336 KiB After Width: | Height: | Size: 336 KiB |
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 492 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 250 KiB After Width: | Height: | Size: 250 KiB |
@@ -11,6 +11,8 @@ export const events = Router()
|
||||
events.get("/", async (req: Request, res: Response) => {
|
||||
let cityName = req.query.city
|
||||
let genreName = req.query.genre
|
||||
let sort = req.query.sort
|
||||
let count = req.query.count
|
||||
let cityFilter = {}
|
||||
let genreFilter = {}
|
||||
|
||||
@@ -44,14 +46,16 @@ events.get("/", async (req: Request, res: Response) => {
|
||||
include: [
|
||||
{
|
||||
model: Location,
|
||||
required: true,
|
||||
include: [
|
||||
cityFilter
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
model: Band,
|
||||
required: true,
|
||||
include: [
|
||||
genreFilter
|
||||
]
|
||||
@@ -59,21 +63,21 @@ events.get("/", async (req: Request, res: Response) => {
|
||||
]
|
||||
})
|
||||
.then(events => {
|
||||
let resultArray = []
|
||||
|
||||
// Remove datasets which not fulfill the optional parameter
|
||||
for (let event of events) {
|
||||
if (event.dataValues.band != null) {
|
||||
for (let concert of event.dataValues.concerts) {
|
||||
if (concert.dataValues.location != null) {
|
||||
resultArray.push(event)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (sort != undefined) {
|
||||
events.sort((event1, event2) => {
|
||||
if (sort == "desc") {
|
||||
return event2.dataValues.concerts.length - event1.dataValues.concerts.length
|
||||
} else if (sort == "asc") {
|
||||
return event1.dataValues.concerts.length - event2.dataValues.concerts.length
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
res.status(200).json(resultArray)
|
||||
if (count != undefined) {
|
||||
events.splice(Number(count))
|
||||
}
|
||||
|
||||
res.status(200).json(events)
|
||||
})
|
||||
|
||||
})
|
||||
@@ -12,6 +12,9 @@ import { Op } from "sequelize";
|
||||
export const location = Router()
|
||||
|
||||
location.get("/", (req: Request, res: Response) => {
|
||||
let sort = req.query.sort
|
||||
let count = req.query.count
|
||||
|
||||
Location.findAll({
|
||||
include: [
|
||||
City,
|
||||
@@ -49,13 +52,25 @@ location.get("/", (req: Request, res: Response) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (sort != undefined) {
|
||||
locations.sort((location1, location2) => {
|
||||
if (sort == "desc") {
|
||||
return location2.dataValues.concerts.length - location1.dataValues.concerts.length
|
||||
} else if (sort == "asc") {
|
||||
return location1.dataValues.concerts.length - location2.dataValues.concerts.length
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (count != undefined) {
|
||||
locations.splice(Number(count))
|
||||
}
|
||||
|
||||
res.status(200).json(locations)
|
||||
})
|
||||
})
|
||||
|
||||
location.get("/:name", (req: Request, res: Response) => {
|
||||
console.log(req.params.name)
|
||||
|
||||
Location.findOne({
|
||||
where: { name: { [Op.like]: req.params.name } },
|
||||
include: [
|
||||
|
||||
@@ -15,7 +15,7 @@ defineProps({
|
||||
<v-col>
|
||||
<card-with-left-image
|
||||
:title="title"
|
||||
:image="'http://localhost:3000/static/tours/' + image"
|
||||
:image="'http://localhost:3000/static/' + image"
|
||||
>
|
||||
<div class="text-body-1 font-weight-bold">
|
||||
<div v-if="!$slots.description">
|
||||
|
||||
@@ -9,3 +9,9 @@ export async function fetchEvents(city: string = "", genre: string = "") {
|
||||
|
||||
return await axios.get(url)
|
||||
}
|
||||
|
||||
export async function getTopEvents(nrOfEvents) {
|
||||
let url = BASE_URL + "?sort=desc&count=" + nrOfEvents
|
||||
|
||||
return await axios.get(url)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from "axios"
|
||||
|
||||
let BASE_URL = "http://localhost:3000/locations"
|
||||
const BASE_URL = "http://localhost:3000/locations"
|
||||
|
||||
export async function getAllLocations() {
|
||||
return await axios.get(BASE_URL)
|
||||
@@ -9,3 +9,9 @@ export async function getAllLocations() {
|
||||
export async function getLocation(locationName: string) {
|
||||
return await axios.get(BASE_URL + "/" + locationName)
|
||||
}
|
||||
|
||||
export async function getTopLocations(nrOfLocations: number) {
|
||||
let url = BASE_URL + "?sort=desc&count=" + nrOfLocations
|
||||
|
||||
return await axios.get(url)
|
||||
}
|
||||
@@ -3,12 +3,33 @@ import { useConcertStore } from '@/data/stores/concertStore';
|
||||
import highlightCarousel from './highlightCarousel.vue';
|
||||
import sectionDivider from '@/components/sectionDivider.vue';
|
||||
import cardWithTopImage from '@/components/cardWithTopImage.vue';
|
||||
import { calcRating, lowestTicketPrice } from '@/scripts/concertScripts';
|
||||
import { lowestTicketPrice } from '@/scripts/concertScripts';
|
||||
import OutlinedButton from '@/components/outlinedButton.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useFeedbackStore } from '@/data/stores/feedbackStore';
|
||||
import { ref } from 'vue';
|
||||
import { EventModel } from '@/data/models/acts/eventModel';
|
||||
import { getTopEvents } from '@/data/api/eventApi';
|
||||
import { LocationModel } from '@/data/models/locations/locationModel';
|
||||
import { getTopLocations } from '@/data/api/locationApi';
|
||||
|
||||
const router = useRouter()
|
||||
const concertStore = useConcertStore()
|
||||
const feedbackStore = useFeedbackStore()
|
||||
const topEvents = ref<Array<EventModel>>(Array.from({length: 4}, () => new EventModel()))
|
||||
const topLocations = ref<Array<LocationModel>>(Array.from({length: 8}, () => new LocationModel()))
|
||||
|
||||
feedbackStore.fetchDataFromServerInProgress = true
|
||||
|
||||
getTopEvents(4)
|
||||
.then(events => {
|
||||
topEvents.value = events.data
|
||||
|
||||
getTopLocations(8)
|
||||
.then(locations => {
|
||||
topLocations.value = locations.data
|
||||
feedbackStore.fetchDataFromServerInProgress = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -28,12 +49,13 @@ const concertStore = useConcertStore()
|
||||
<v-row>
|
||||
<v-col v-for="i in 4" cols="3">
|
||||
<card-with-top-image
|
||||
:image="'tours/' + concertStore.tours[i - 1].image"
|
||||
:title="concertStore.tours[i - 1].band.name"
|
||||
:image="topEvents[i - 1].image"
|
||||
:title="topEvents[i - 1].band.name"
|
||||
smaller-title
|
||||
@click="router.push('/bands/' + concertStore.tours[i - 1].band.name.replaceAll(' ', '-').toLowerCase())"
|
||||
@click="router.push('/bands/' + topEvents[i - 1].band.name.replaceAll(' ', '-').toLowerCase())"
|
||||
:loading="feedbackStore.fetchDataFromServerInProgress"
|
||||
>
|
||||
ab {{ lowestTicketPrice(concertStore.tours[i - 1]) }} €
|
||||
ab {{ lowestTicketPrice(topEvents[i - 1]) }} €
|
||||
</card-with-top-image>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -59,12 +81,13 @@ const concertStore = useConcertStore()
|
||||
<v-row>
|
||||
<v-col v-for="i in 8" cols="3">
|
||||
<card-with-top-image
|
||||
:image="'locations/' + concertStore.locations[i + 2].image"
|
||||
:title="concertStore.locations[i + 2].name"
|
||||
:image="topLocations[i - 1].image"
|
||||
:title="topLocations[i - 1].name"
|
||||
smaller-title
|
||||
@click="router.push('/locations/' + concertStore.locations[i + 2].name.replaceAll(' ', '-').toLowerCase())"
|
||||
@click="router.push('/locations/' + topLocations[i - 1].name.replaceAll(' ', '-').toLowerCase())"
|
||||
:loading="feedbackStore.fetchDataFromServerInProgress"
|
||||
>
|
||||
{{ concertStore.locations[i + 2].city.name }}, {{ concertStore.locations[i + 2].city.country }}
|
||||
{{ topLocations[i - 1].city.name }}, {{ topLocations[i - 1].city.country }}
|
||||
</card-with-top-image>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||