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
|
#### Listing existing
|
||||||
|
|
||||||
<details>
|
<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>
|
</summary>
|
||||||
|
|
||||||
##### Parameters
|
##### 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 |
|
> | `cityName` | optional | string | Name of the city to filter for |
|
||||||
> | `genreName` | optional | string | Name of the genre 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
|
##### Responses
|
||||||
> | http code | content-type | response |
|
> | 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>
|
||||||
|
|
||||||
|
|
||||||
|
<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!
|
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",
|
"name": "Unlimited Love",
|
||||||
"bandId": 0,
|
"bandId": 0,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"image": "unlimited-love-tour.jpg",
|
"image": "events/unlimited-love-tour.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 0,
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
"name": "The Bends",
|
"name": "The Bends",
|
||||||
"bandId": 1,
|
"bandId": 1,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"image": "the-bends-tour.jpg",
|
"image": "events/the-bends-tour.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 5,
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"name": "European Tour",
|
"name": "European Tour",
|
||||||
"bandId": 2,
|
"bandId": 2,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"image": "european-tour-arctic-monkeys.jpg",
|
"image": "events/european-tour-arctic-monkeys.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 8,
|
"id": 8,
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
"name": "Music of the Spheres",
|
"name": "Music of the Spheres",
|
||||||
"bandId": 3,
|
"bandId": 3,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"image": "music-of-the-spheres.png",
|
"image": "events/music-of-the-spheres.png",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 10,
|
"id": 10,
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
"name": "But Here We Are Tour",
|
"name": "But Here We Are Tour",
|
||||||
"bandId": 4,
|
"bandId": 4,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"image": "but-here-we-are.jpg",
|
"image": "events/but-here-we-are.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 13,
|
"id": 13,
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
"name": "Crisis of Faith",
|
"name": "Crisis of Faith",
|
||||||
"bandId": 5,
|
"bandId": 5,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"image": "crisis-of-faith-tour.jpg",
|
"image": "events/crisis-of-faith-tour.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 14,
|
"id": 14,
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
"name": "Back to the Water Below",
|
"name": "Back to the Water Below",
|
||||||
"bandId": 6,
|
"bandId": 6,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"image": "back-to-the-water-below.jpg",
|
"image": "events/back-to-the-water-below.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 16,
|
"id": 16,
|
||||||
@@ -194,7 +194,7 @@
|
|||||||
"name": "Will of the People Tour",
|
"name": "Will of the People Tour",
|
||||||
"bandId": 7,
|
"bandId": 7,
|
||||||
"offered": true,
|
"offered": true,
|
||||||
"image": "will-of-the-people-tour.jpg",
|
"image": "events/will-of-the-people-tour.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"id": 18,
|
"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) => {
|
events.get("/", async (req: Request, res: Response) => {
|
||||||
let cityName = req.query.city
|
let cityName = req.query.city
|
||||||
let genreName = req.query.genre
|
let genreName = req.query.genre
|
||||||
|
let sort = req.query.sort
|
||||||
|
let count = req.query.count
|
||||||
let cityFilter = {}
|
let cityFilter = {}
|
||||||
let genreFilter = {}
|
let genreFilter = {}
|
||||||
|
|
||||||
@@ -44,14 +46,16 @@ events.get("/", async (req: Request, res: Response) => {
|
|||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: Location,
|
model: Location,
|
||||||
|
required: true,
|
||||||
include: [
|
include: [
|
||||||
cityFilter
|
cityFilter
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: Band,
|
model: Band,
|
||||||
|
required: true,
|
||||||
include: [
|
include: [
|
||||||
genreFilter
|
genreFilter
|
||||||
]
|
]
|
||||||
@@ -59,21 +63,21 @@ events.get("/", async (req: Request, res: Response) => {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
.then(events => {
|
.then(events => {
|
||||||
let resultArray = []
|
if (sort != undefined) {
|
||||||
|
events.sort((event1, event2) => {
|
||||||
// Remove datasets which not fulfill the optional parameter
|
if (sort == "desc") {
|
||||||
for (let event of events) {
|
return event2.dataValues.concerts.length - event1.dataValues.concerts.length
|
||||||
if (event.dataValues.band != null) {
|
} else if (sort == "asc") {
|
||||||
for (let concert of event.dataValues.concerts) {
|
return event1.dataValues.concerts.length - event2.dataValues.concerts.length
|
||||||
if (concert.dataValues.location != null) {
|
|
||||||
resultArray.push(event)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
export const location = Router()
|
||||||
|
|
||||||
location.get("/", (req: Request, res: Response) => {
|
location.get("/", (req: Request, res: Response) => {
|
||||||
|
let sort = req.query.sort
|
||||||
|
let count = req.query.count
|
||||||
|
|
||||||
Location.findAll({
|
Location.findAll({
|
||||||
include: [
|
include: [
|
||||||
City,
|
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)
|
res.status(200).json(locations)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
location.get("/:name", (req: Request, res: Response) => {
|
location.get("/:name", (req: Request, res: Response) => {
|
||||||
console.log(req.params.name)
|
|
||||||
|
|
||||||
Location.findOne({
|
Location.findOne({
|
||||||
where: { name: { [Op.like]: req.params.name } },
|
where: { name: { [Op.like]: req.params.name } },
|
||||||
include: [
|
include: [
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ defineProps({
|
|||||||
<v-col>
|
<v-col>
|
||||||
<card-with-left-image
|
<card-with-left-image
|
||||||
:title="title"
|
:title="title"
|
||||||
:image="'http://localhost:3000/static/tours/' + image"
|
:image="'http://localhost:3000/static/' + image"
|
||||||
>
|
>
|
||||||
<div class="text-body-1 font-weight-bold">
|
<div class="text-body-1 font-weight-bold">
|
||||||
<div v-if="!$slots.description">
|
<div v-if="!$slots.description">
|
||||||
|
|||||||
@@ -9,3 +9,9 @@ export async function fetchEvents(city: string = "", genre: string = "") {
|
|||||||
|
|
||||||
return await axios.get(url)
|
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"
|
import axios from "axios"
|
||||||
|
|
||||||
let BASE_URL = "http://localhost:3000/locations"
|
const BASE_URL = "http://localhost:3000/locations"
|
||||||
|
|
||||||
export async function getAllLocations() {
|
export async function getAllLocations() {
|
||||||
return await axios.get(BASE_URL)
|
return await axios.get(BASE_URL)
|
||||||
@@ -9,3 +9,9 @@ export async function getAllLocations() {
|
|||||||
export async function getLocation(locationName: string) {
|
export async function getLocation(locationName: string) {
|
||||||
return await axios.get(BASE_URL + "/" + locationName)
|
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 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, lowestTicketPrice } from '@/scripts/concertScripts';
|
import { lowestTicketPrice } from '@/scripts/concertScripts';
|
||||||
import OutlinedButton from '@/components/outlinedButton.vue';
|
import OutlinedButton from '@/components/outlinedButton.vue';
|
||||||
import { useRouter } from 'vue-router';
|
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 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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -28,12 +49,13 @@ const concertStore = useConcertStore()
|
|||||||
<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="'tours/' + concertStore.tours[i - 1].image"
|
:image="topEvents[i - 1].image"
|
||||||
:title="concertStore.tours[i - 1].band.name"
|
:title="topEvents[i - 1].band.name"
|
||||||
smaller-title
|
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>
|
</card-with-top-image>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -59,12 +81,13 @@ const concertStore = useConcertStore()
|
|||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="i in 8" cols="3">
|
<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="topLocations[i - 1].image"
|
||||||
:title="concertStore.locations[i + 2].name"
|
:title="topLocations[i - 1].name"
|
||||||
smaller-title
|
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>
|
</card-with-top-image>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|||||||