Refactor frontend, display tours with cards on ToursPage

This commit is contained in:
2024-09-26 16:06:20 +02:00
parent f5204578e4
commit 941fd711d5
39 changed files with 397 additions and 349 deletions

View File

@@ -1,6 +1,6 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.7.8 Chrome/128.0.6613.36 Electron/32.0.1 Safari/537.36" version="24.7.8">
<diagram name="Page-1" id="WevClHWmhzPAQ7FDN5po">
<mxGraphModel dx="3113" dy="447" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<mxGraphModel dx="3263" dy="534" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
@@ -572,7 +572,7 @@
</mxCell>
<mxCell id="EQeajuEG8KHzwlrw_xps-164" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="EQeajuEG8KHzwlrw_xps-163" connectable="0" vertex="1">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="-22" y="-28" as="offset" />
<mxPoint x="3" y="-29" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="EQeajuEG8KHzwlrw_xps-165" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="EQeajuEG8KHzwlrw_xps-163" connectable="0" vertex="1">

View File

@@ -38,6 +38,50 @@
"bandId": 0
}
]
},
{
"id": 1,
"name": "Radiohead",
"foundingYear": 1985,
"descriptionEn": "Radiohead are an English rock band formed in Abingdon, Oxfordshire, in 1985. They comprise Thom Yorke (vocals, guitar, piano, keyboards); brothers Jonny Greenwood (guitar, keyboards, other instruments) and Colin Greenwood (bass); Ed O'Brien (guitar, backing vocals); and Philip Selway (drums, percussion). They have worked with the producer Nigel Godrich and the cover artist Stanley Donwood since 1994. Radiohead's experimental approach is credited with advancing the sound of alternative rock.",
"descriptionDe": "Radiohead ist eine britische Rockband, die 1985 in Oxford, England gegründet wurde. Die Band besteht aus Thom Yorke (Gesang, Rhythmusgitarre, Piano), Jonny Greenwood (Lead-Gitarre, Keyboard, Ondes Martenot), Colin Greenwood (E-Bass, Keyboard), Ed OBrien (Gitarre, Backgroundvocals) und Phil Selway (Schlagzeug, Backgroundvocals). Radioheads experimenteller Ansatz gilt als Wegbereiter für den Sound des Alternative Rocks.",
"images": [ "radiohead.jpg" ],
"logo": "radiohead-logo.png",
"genreId": 1,
"members": [
{
"name": "Thom Yorke",
"bandId": 1,
"image": "thom-yorke.jpg"
},
{
"name": "Jonny Greenwood",
"bandId": 1,
"image": "jonny-greenwood.jpg"
},
{
"name": "Colin Greenwood",
"bandId": 1,
"image": "colin-greenwood.jpg"
},
{
"name": "Ed O'Brien",
"bandId": 1,
"image": "ed-o-brien.jpg"
},
{
"name": "Philip Selway",
"bandId": 1,
"image": "philip-selway.jpg"
}
],
"ratings": [
{
"accountId": 0,
"rating": 5,
"bandId": 1
}
]
}
]
}

View File

@@ -3,6 +3,10 @@
{
"id": 0,
"name": "Funk rock"
},
{
"id": 1,
"name": "Art rock"
}
]
}

View File

@@ -15,6 +15,30 @@
"tourId": 0
}
]
},
{
"id": 1,
"name": "The Bends Tour",
"bandId": 1,
"offered": true,
"shows": [
{
"id": 1,
"date": "2024-11-30",
"price": 104,
"inStock": 120,
"locationId": 0,
"tourId": 1
},
{
"id": 2,
"date": "2024-12-01",
"price": 104,
"inStock": 180,
"locationId": 0,
"tourId": 1
}
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -4,21 +4,19 @@ import { i18n } from './plugins/i18n';
import { ref, watch } from 'vue';
import vuetify from './plugins/vuetify';
import navigationItems from './components/navigationItems.vue';
import { useProductStore } from './data/stores/productStore';
import { usePreferencesStore } from './data/stores/preferencesStore';
import { useFeedbackStore } from './data/stores/feedbackStore';
import { useTourStore } from './data/stores/tourStore';
const preferencesStore = usePreferencesStore()
const productStore = useProductStore()
const tourStore = useTourStore()
const feedbackStore = useFeedbackStore()
const theme = useTheme()
const navRail = ref(vuetify.display.mobile)
theme.global.name.value = preferencesStore.theme
productStore.fetchAllProducts()
productStore.fetchAllCategories()
productStore.fetchAllBrands()
tourStore.fetchAllTours()
// Global watcher
watch(() => preferencesStore.language, () => {

View File

@@ -7,28 +7,72 @@ defineProps({
},
subtitle: {
type: String,
},
prependImage: {
type: String,
default: ""
}
})
</script>
<template>
<v-card
:title="title"
:subtitle="subtitle"
:prepend-icon="icon"
<v-card>
<v-row v-if="prependImage != ''">
<v-col cols="3" class="pr-0">
<v-img
:src="prependImage"
height="220"
cover
>
<!-- Show default container only, if there is content -->
<v-container v-if="$slots.default">
<template #placeholder>
<v-skeleton-loader
type="image"
height="300"
cover
/>
</template>
</v-img>
</v-col>
<v-col class="pl-0" >
<v-card-title>
{{ title }}
</v-card-title>
<v-card-subtitle>
{{ subtitle }}
</v-card-subtitle>
<div class="pa-4">
<slot></slot>
</v-container>
</div>
<!-- Slot for content without padding -->
<slot name="withoutContainer"></slot>
<!-- Slot for Action Buttons in the right bottom corner -->
<v-card-actions v-if="$slots.actions" class="card-actions">
<v-card-actions v-if="$slots.actions" class="card-actions position-absolute bottom-0 right-0">
<v-spacer />
<slot name="actions"></slot>
</v-card-actions>
</v-col>
</v-row>
<v-container v-else>
<slot></slot>
</v-container>
<!-- Show default container only, if there is content -->
<!-- <v-container v-if="$slots.default">
<slot></slot>
</v-container> -->
<!-- Slot for content without padding -->
<!-- <slot name="withoutContainer"></slot> -->
<!-- Slot for Action Buttons in the right bottom corner -->
<!-- <v-card-actions v-if="$slots.actions" class="card-actions">
<v-spacer />
<slot name="actions"></slot>
</v-card-actions> -->
</v-card>
</template>

View File

@@ -12,12 +12,12 @@ const navRail = defineModel("navRail", { type: Boolean })
<!-- Shopping Section -->
<v-list-subheader>
<div v-if="!navRail">{{ $t('menu.shopping') }}</div>
<div v-if="!navRail">{{ $t('menu.shopping.shopping') }}</div>
<div v-else></div>
</v-list-subheader>
<v-list-item :title="$t('menu.products')" prepend-icon="mdi-store" to="/" link />
<v-list-item :title="$t('menu.basket')" to="/basket" link >
<v-list-item :title="$t('menu.shopping.ticket', 2)" prepend-icon="mdi-ticket" to="/" link />
<v-list-item :title="$t('menu.shopping.basket')" to="/basket" link >
<template v-slot:prepend>
<v-badge color="primary" :content="basketStore.itemsInBasket.length">
<v-icon icon="mdi-cart" />
@@ -31,21 +31,21 @@ const navRail = defineModel("navRail", { type: Boolean })
<!-- Account Section -->
<v-list-subheader>
<div v-if="!navRail">{{ $t('menu.account') }}</div>
<div v-if="!navRail">{{ $t('menu.account.accountManagement') }}</div>
<div v-else></div>
</v-list-subheader>
<v-expand-transition>
<div v-if="accountStore.userAccount.id == null">
<v-list-item v-if="accountStore.userAccount.id == null" :title="$t('menu.login')" prepend-icon="mdi-login" to="/login" link />
<v-list-item v-if="accountStore.userAccount.id == null" :title="$t('menu.account.login')" prepend-icon="mdi-login" to="/login" link />
</div>
</v-expand-transition>
<v-expand-transition>
<div v-if="accountStore.userAccount.id != null">
<v-list-item :title="$t('menu.account')" prepend-icon="mdi-account" to="/account" link />
<v-list-item :title="$t('menu.orders')" prepend-icon="mdi-cart-check" to="/orders" link />
<v-list-item :title="$t('menu.logout')" prepend-icon="mdi-logout" @click="accountStore.logout" link />
<v-list-item :title="$t('menu.account.account')" prepend-icon="mdi-account" to="/account" link />
<v-list-item :title="$t('menu.account.order', 2)" prepend-icon="mdi-cart-check" to="/orders" link />
<v-list-item :title="$t('menu.account.logout')" prepend-icon="mdi-logout" @click="accountStore.logout" link />
</div>
</v-expand-transition>
@@ -55,12 +55,12 @@ const navRail = defineModel("navRail", { type: Boolean })
<!-- System and help section -->
<v-list-subheader>
<div v-if="!navRail">{{ $t('menu.systemAndHelp') }}</div>
<div v-if="!navRail">{{ $t('menu.systemAndHelp.systemAndHelp') }}</div>
<div v-else></div>
</v-list-subheader>
<v-list-item :title="$t('menu.helpInstructions')" prepend-icon="mdi-chat-question" to="/help" link />
<v-list-item :title="$t('menu.scoreBoard')" prepend-icon="mdi-podium-gold" to="/scoreBoard" link />
<v-list-item :title="$t('menu.preferences')" prepend-icon="mdi-cog" to="/preferences" link />
<v-list-item :title="$t('menu.systemAndHelp.helpInstructions')" prepend-icon="mdi-chat-question" to="/help" link />
<v-list-item :title="$t('menu.systemAndHelp.scoreBoard')" prepend-icon="mdi-podium-gold" to="/scoreBoard" link />
<v-list-item :title="$t('menu.systemAndHelp.preferences')" prepend-icon="mdi-cog" to="/preferences" link />
<div v-if="accountStore.userAccount.accountRole.privilegeAdminPanel">
@@ -72,10 +72,6 @@ const navRail = defineModel("navRail", { type: Boolean })
</v-list-subheader>
<v-list-item :title="$t('menu.admin.dashboard')" prepend-icon="mdi-view-dashboard" to="/admin/dashboard" link />
<v-list-item :title="$t('menu.admin.categories')" prepend-icon="mdi-label" to="/admin/categories" link />
<v-list-item :title="$t('brand', 2)" prepend-icon="mdi-factory" to="/admin/brands" link />
<v-list-item :title="$t('menu.admin.products')" prepend-icon="mdi-store-cog" to="/admin/products" link />
<v-list-item :title="$t('menu.admin.accounts')" prepend-icon="mdi-account-multiple" to="/admin/accounts" link />
</div>
</v-list>

View File

@@ -0,0 +1,11 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/bands"
export async function getAllBands() {
return await axios.get(BASE_URL)
}
export async function getOneBand(id: number) {
return await axios.get(BASE_URL + '/' + id)
}

View File

@@ -1,7 +0,0 @@
import axios from "axios";
let BASE_URL = "http://localhost:3000/brands"
export async function getAllBrands() {
return await axios.get(BASE_URL)
}

View File

@@ -1,7 +0,0 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/categories"
export async function getAllCategories() {
return await axios.get(BASE_URL)
}

View File

@@ -0,0 +1,7 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/genres"
export async function getAllGenres() {
return await axios.get(BASE_URL)
}

View File

@@ -0,0 +1,7 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/locations"
export async function getAllLocations() {
return await axios.get(BASE_URL)
}

View File

@@ -1,10 +0,0 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/products"
/**
* Fetch all products from API
*/
export async function getAllProducts() {
return await axios.get(BASE_URL)
}

View File

@@ -0,0 +1,7 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/shows"
export async function getAllShows() {
return await axios.get(BASE_URL)
}

View File

@@ -0,0 +1,10 @@
import axios from "axios"
let BASE_URL = "http://localhost:3000/tours"
/**
* Fetch all tours from API
*/
export async function getAllTours() {
return await axios.get(BASE_URL)
}

View File

@@ -1,11 +1,11 @@
import { ProductModel } from "./productModel"
import { ShowModel } from "./showModel"
export class BasketItemModel {
id: number = -1
quantity: number = 1
product: ProductModel = new ProductModel()
product: ShowModel = new ShowModel()
constructor(quantity: number, product: ProductModel) {
constructor(quantity: number, product: ShowModel) {
this.quantity = quantity
this.product = product
}

View File

@@ -0,0 +1,5 @@
export class CityModel {
id: Number
name: String
country: String
}

View File

@@ -1,7 +1,9 @@
import { CityModel } from "./cityModel"
export class LocationModel {
id: Number
name: String
address: String
city: String
city: CityModel
image: String
}

View File

@@ -3,6 +3,5 @@ import { BandModel } from "./bandModel"
export class MemberModel {
id: Number
name: String
band: BandModel
image: String
}

View File

@@ -3,7 +3,6 @@ import { BandModel } from "./bandModel"
export class RatingModel {
id: Number
account: AccountModel
rating: Number
band: BandModel
}

View File

@@ -2,9 +2,9 @@ import { BandModel } from "./bandModel"
import { ShowModel } from "./showModel"
export class TourModel {
id: Number
name: String
id: number
name: string
offered: boolean
band: BandModel
offered: Boolean
shows: Array<ShowModel>
}

View File

@@ -6,8 +6,7 @@ import { useFeedbackStore } from "./feedbackStore";
import { BannerStateEnum } from "../enums/bannerStateEnum";
import { addOrder } from "../api/orderApi";
import { useAccountStore } from "./accountStore";
import { ProductModel } from "../models/productModel";
import { useProductStore } from "./productStore";
import { ShowModel } from "../models/showModel";
import { AddressModel } from "../models/addressModel";
import { PaymentModel } from "../models/paymentModel";
@@ -53,21 +52,21 @@ export const useBasketStore = defineStore('basketStore', {
/**
* Add an item to the basket. If the product is already in the basket, the quantity will increase
*
* @param product Product to add
* @param show Show to add
* @param quantity Quantity of the product
*/
addItemToBasket(product: ProductModel, quantity: number) {
addItemToBasket(show: ShowModel, quantity: number) {
const feedbackStore = useFeedbackStore()
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTADDED)
// Product is already in the basket, increase number of items
if (this.itemsInBasket.find((basketItem: BasketItemModel) =>
basketItem.product.id == product.id))
basketItem.product.id == show.id))
{
this.itemsInBasket.find((basketItem: BasketItemModel) =>
basketItem.product.id == product.id).quantity += quantity
basketItem.product.id == show.id).quantity += quantity
} else {
this.itemsInBasket.push(new BasketItemModel(quantity, product))
this.itemsInBasket.push(new BasketItemModel(quantity, show))
}
},
@@ -75,22 +74,23 @@ export const useBasketStore = defineStore('basketStore', {
* Take an order to the server. Sends all articles in the basket and creates an order entry in the backend database
*/
async takeOrder() {
const accountStore = useAccountStore()
const productStore = useProductStore()
const feedbackStore = useFeedbackStore()
// todo
// const accountStore = useAccountStore()
// const productStore = useProductStore()
// const feedbackStore = useFeedbackStore()
await addOrder(accountStore.userAccount.id, this.itemsInBasket, this.usedPayment.id, this.usedAddress.id)
.then(async result => {
if (result.status == 201) {
await accountStore.refreshOrders()
await productStore.fetchAllProducts()
// await addOrder(accountStore.userAccount.id, this.itemsInBasket, this.usedPayment.id, this.usedAddress.id)
// .then(async result => {
// if (result.status == 201) {
// await accountStore.refreshOrders()
// await productStore.fetchAllProducts()
this.itemsInBasket = []
feedbackStore.changeBanner(BannerStateEnum.ORDERPLACESUCCESSFUL)
} else {
feedbackStore.changeBanner(BannerStateEnum.ERROR)
}
})
// this.itemsInBasket = []
// feedbackStore.changeBanner(BannerStateEnum.ORDERPLACESUCCESSFUL)
// } else {
// feedbackStore.changeBanner(BannerStateEnum.ERROR)
// }
// })
}
}
})

View File

@@ -1,92 +0,0 @@
import { useLocalStorage } from "@vueuse/core";
import { defineStore } from "pinia";
import { getAllProducts } from "../api/productApi";
import { SortOrder } from "../enums/sortOrderEnum";
import { CategoryModel } from "../models/categoryModel";
import { ProductModel } from "../models/productModel";
import { BrandModel } from "../models/brandModel";
import { getAllCategories } from "../api/categoryApi";
import { getAllBrands } from "../api/brandApi";
export const useProductStore = defineStore("productStore", {
state: () => ({
products: useLocalStorage<Array<ProductModel>>("hackmycart/productStore/products", []),
filteredProducts: useLocalStorage<Array<ProductModel>>("hackmycart/productStore/filteredProducts", []),
sortOrder: useLocalStorage<SortOrder>("hackmycart/productStore/sortOrder", SortOrder.NAMEATOZ),
filteredCategory: useLocalStorage<CategoryModel>("hackmycart/productStore/filteredCategory", new CategoryModel()),
onlyDiscounts: useLocalStorage<Boolean>("hackmycart/productStore/onlyDiscounts", false),
brands: useLocalStorage<Array<BrandModel>>("hackmycart/productStore/brands", []),
categories: useLocalStorage<Array<CategoryModel>>("hackmycart/productStore/categories", [])
}),
actions: {
async fetchAllProducts() {
await getAllProducts()
.then(products => {
this.products = products.data
this.filteredProducts = products.data
})
},
async fetchAllCategories() {
await getAllCategories()
.then(categories => {
this.categories = categories.data
})
},
async fetchAllBrands() {
await getAllBrands()
.then(brands => {
this.brands = brands.data
})
},
async filterProducts() {
if (this.filteredCategory.id == -1 || this.filteredCategory.id == 0) {
this.filteredProducts = this.products
} else {
this.filteredProducts = this.products.filter((product: ProductModel) =>
product.category.id == this.filteredCategory.id
)
}
if (this.onlyDiscounts) {
this.filteredProducts = this.filteredProducts.filter((product: ProductModel) =>
product.discount > 0
)
}
},
sortProducts() {
this.filteredProducts.sort((a: ProductModel, b: ProductModel) => {
switch (this.sortOrder)
{
case SortOrder.PRICELOWTOHIGH: {
return a.price - b.price
}
case SortOrder.PRICEHIGHTOLOW: {
return b.price - a.price
}
case SortOrder.NAMEATOZ: {
if (b.name > a.name) {
return -1
}
else {
return 1
}
}
case SortOrder.NAMEZTOA: {
if (b.name < a.name) {
return -1
}
else {
return 1
}
}
}
})
}
}
})

View File

@@ -0,0 +1,21 @@
import { useLocalStorage } from "@vueuse/core";
import { defineStore } from "pinia";
import { TourModel } from "../models/tourModel";
import { getAllTours } from "../api/tourApi";
import { GenreModel } from "../models/genreModel";
export const useTourStore = defineStore("tourStore", {
state: () => ({
tours: useLocalStorage<Array<TourModel>>("hackmycart/tourStore/tours", []),
genres: useLocalStorage<Array<GenreModel>>("hackmycart/tourStore/genres", [])
}),
actions: {
async fetchAllTours() {
await getAllTours()
.then(result => {
this.tours = result.data
})
}
}
})

View File

@@ -1,24 +1,32 @@
{
"menu": {
"shopping": {
"shopping": "Einkaufen",
"products": "Produkte",
"basket": "Warenkorb",
"login": "Login",
"ticket": "Ticket | Tickets",
"basket": "Warenkorb"
},
"account": {
"accountManagement": "Kontoverwaltung",
"account": "Account",
"orders": "Bestellungen",
"login": "Login",
"order": "Bestellung | Bestellungen",
"logout": "Ausloggen"
},
"systemAndHelp": {
"systemAndHelp": "System & Hilfe",
"helpInstructions": "Hilfestellung",
"preferences": "Einstellungen",
"logout": "Ausloggen",
"scoreBoard": "Score Board",
"scoreBoard": "Score Board"
},
"admin": {
"admin": "Administration",
"dashboard": "Dashboard",
"categories": "Kategorien",
"products": "Produkte",
"accounts": "Accounts"
"dashboard": "Dashboard"
}
},
"tours": {
"concert": "Konzert | Konzerte"
},
"preferences": {
"pageSetup": "Seiteneinstellungen",
"selectedTheme": "Ausgewähltes Theme",

View File

@@ -1,24 +1,33 @@
{
"menu": {
"shopping": {
"shopping": "Shopping",
"products": "Products",
"basket": "Basket",
"ticket": "Ticket | Tickets",
"basket": "Basket"
},
"account": {
"account": "Account Management",
"login": "Login",
"account": "Account",
"orders": "Orders",
"systemAndHelp": "System & Help",
"helpInstructions": "Help instructions",
"preferences": "Preferences",
"order": "Order | Orders",
"logout": "Logout",
"scoreBoard": "Score Board",
"accountManagement": "Account Management"
},
"systemAndHelp": {
"systemAndHelp": "System & Help",
"helpInstructions": "Help Instructions",
"preferences": "Preferences",
"scoreBoard": "Score Board"
},
"admin": {
"admin": "Administration",
"dashboard": "Dashboard",
"categories": "Categories",
"products": "Products",
"accounts": "Accounts"
"dashboard": "Dashboard"
}
},
"tours": {
"concert": "Concert | Concerts"
},
"preferences": {
"pageSetup": "Page setup",
"selectedTheme": "Selected theme",

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
import cardView from '@/components/cardView.vue';
import { useProductStore } from '@/data/stores/productStore';
// import { useProductStore } from '@/data/stores/productStore';
const productStore = useProductStore()
// const productStore = useProductStore()
const headers = [
{ title: "Name", value: "name" },
@@ -11,7 +11,7 @@ const headers = [
</script>
<template>
<v-container max-width="800">
<!-- <v-container max-width="800">
<v-row>
<v-col>
<card-view
@@ -24,9 +24,8 @@ const headers = [
:headers="headers"
>
</v-data-table>
<!-- todo: Edit/Delete -->
</card-view>
</v-col>
</v-row>
</v-container>
</v-container> -->
</template>

View File

@@ -1,8 +1,5 @@
<script setup lang="ts">
import cardView from '@/components/cardView.vue';
import { useProductStore } from '@/data/stores/productStore';
const productStore = useProductStore()
const headers = [
{ title: "Name", value: "name" },
@@ -12,24 +9,5 @@ const headers = [
</script>
<template>
<v-container max-width="800">
<v-row>
<v-col>
<card-view
:title="$t('category', 2)"
icon="mdi-label"
:subtitle="productStore.categories.length + ' ' + $t('category', productStore.categories.length)"
>
<v-data-table
:items="productStore.categories"
:headers="headers"
>
<template v-slot:item.icon="{ item }">
<v-icon :icon="item.icon" />
</template>
</v-data-table>
</card-view>
</v-col>
</v-row>
</v-container>
</template>

View File

@@ -1,12 +1,10 @@
<script setup lang="ts">
import cardView from '@/components/cardView.vue';
import { ProductModel } from '@/data/models/productModel';
import { useProductStore } from '@/data/stores/productStore';
import productEditDialog from './productEditDialog.vue';
import { ref } from 'vue';
const productStore = useProductStore()
const editProduct = ref(new ProductModel())
// const productStore = useProductStore()
// const editProduct = ref(new ProductModel())
const showEditProductDialog = ref(false)
const headers = [
@@ -20,16 +18,16 @@ const headers = [
{ title: "Edit", value: "edit" },
]
function openEditProductDialog(product: ProductModel) {
editProduct.value = product
showEditProductDialog.value = true
}
// function openEditProductDialog(product: ProductModel) {
// editProduct.value = product
// showEditProductDialog.value = true
// }
</script>
<template>
<v-container>
<v-row>
<v-col>
<!-- <v-col>
<card-view
:title="$t('product.product', 2)"
:subtitle="productStore.products.length + ' ' + $t('product.product', productStore.products.length)"
@@ -88,11 +86,11 @@ function openEditProductDialog(product: ProductModel) {
</template>
</v-data-table>
</card-view>
</v-col>
</v-col> -->
</v-row>
</v-container>
<product-edit-dialog
<!-- <product-edit-dialog
v-model="showEditProductDialog"
:edit-product="editProduct" />
:edit-product="editProduct" /> -->
</template>

View File

@@ -1,53 +0,0 @@
<script setup lang="ts">
import productCard from "./productCard.vue"
import productDetails from "./productDetailsDialog.vue"
import { ref, watch } from "vue";
import { useProductStore } from "@/data/stores/productStore";
import filterNavDrawer from "./filterNavDrawer.vue";
import { ProductModel } from '@/data/models/productModel';
const productStore = useProductStore()
const showProductDetails = ref(false)
const dialogProduct = ref(new ProductModel())
function showProductDialog(product: ProductModel) {
dialogProduct.value = product
showProductDetails.value = true
}
watch(() => productStore.filteredCategory, async () => { productStore.filterProducts() })
watch(() => productStore.sortOrder, async () => { productStore.sortProducts() })
watch(() => productStore.onlyDiscounts, async () => { productStore.filterProducts() })
</script>
<template>
<v-container max-width="1000">
<v-row dense>
<v-col
v-if="productStore.filteredProducts.length > 0"
v-for="product in productStore.filteredProducts"
cols="12"
>
<product-card
:product="product"
@click="showProductDialog(product)"
/>
</v-col>
<v-col v-else>
<v-empty-state
icon="mdi-magnify"
headline="Keine Artikel gefunden"
/>
</v-col>
</v-row>
</v-container>
<filter-nav-drawer />
<product-details
v-model="showProductDetails"
:product="dialogProduct"
/>
</template>

View File

@@ -0,0 +1,6 @@
<script setup lang="ts">
</script>
<template>
</template>

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
import { SortOrder } from '@/data/enums/sortOrderEnum';
import { useProductStore } from '@/data/stores/productStore';
import { useTourStore } from '@/data/stores/tourStore';
const productStore = useProductStore()
const tourStore = useTourStore()
const sortOrderItems = Object.values(SortOrder)
</script>
@@ -11,15 +11,10 @@ const sortOrderItems = Object.values(SortOrder)
<v-list>
<v-list-subheader>Filter</v-list-subheader>
<v-list-item>
<v-checkbox :label="$t('offers')" v-model="productStore.onlyDiscounts" />
</v-list-item>
<v-list-item>
<!-- <v-list-item>
<v-select
:items="productStore.categories"
:items="tourStore.genres"
:label="$t('category', 2)"
v-model="productStore.filteredCategory"
>
<template v-slot:item="{ props, item }">
<v-list-item v-bind="props" :prepend-icon="item.raw.icon" :title="item.raw.name" />
@@ -29,12 +24,12 @@ const sortOrderItems = Object.values(SortOrder)
<v-list-item :prepend-icon="item.raw.icon" :title="item.raw.name" />
</template>
</v-select>
</v-list-item>
</v-list-item> -->
<v-divider />
<v-list-subheader>Sort</v-list-subheader>
<v-list-item>
<!-- <v-list-item>
<v-select :label="$t('sortBy')" :items="sortOrderItems" v-model="productStore.sortOrder" >
<template v-slot:item="{ props, item }">
<v-list-item v-bind="props" :title="item.title" />
@@ -44,7 +39,7 @@ const sortOrderItems = Object.values(SortOrder)
<v-list-item :title="item.title" />
</template>
</v-select>
</v-list-item>
</v-list-item> -->
</v-list>
</v-navigation-drawer>
</template>

View File

@@ -0,0 +1,50 @@
<script setup lang="ts">
import tourCard from "./tourCard.vue"
import { ref, watch } from "vue";
import filterNavDrawer from "./filterNavDrawer.vue";
import { useTourStore } from "@/data/stores/tourStore";
const tourStore = useTourStore()
const showProductDetails = ref(false)
// const dialogProduct = ref(new ProductModel())
// function showProductDialog(product: ProductModel) {
// dialogProduct.value = product
// showProductDetails.value = true
// }
// watch(() => productStore.filteredCategory, async () => { productStore.filterProducts() })
// watch(() => productStore.sortOrder, async () => { productStore.sortProducts() })
// watch(() => productStore.onlyDiscounts, async () => { productStore.filterProducts() })
</script>
<template>
<v-container max-width="1200">
<v-row dense>
<v-col
v-if="tourStore.tours.length > 0"
v-for="tour in tourStore.tours"
cols="12"
>
<tour-card
:tour="tour"
/>
</v-col>
<v-col v-else>
<v-empty-state
icon="mdi-magnify"
headline="Keine Artikel gefunden"
/>
</v-col>
</v-row>
</v-container>
<filter-nav-drawer />
<!-- <product-details
v-model="showProductDetails"
:product="dialogProduct"
/> -->
</template>

View File

@@ -1,35 +1,33 @@
<script setup lang="ts">
import { ProductModel } from '@/data/models/productModel';
import { TourModel } from '@/data/models/tourModel';
import cardView from '@/components/cardView.vue';
import OutlinedButton from '@/components/outlinedButton.vue';
defineProps({
product: {
tour: {
required: true,
type: ProductModel
type: TourModel
}
})
</script>
<template>
<card-view link>
<template #withoutContainer>
<v-row>
<v-col cols="3">
<v-sheet color="white">
<v-img
:src="'http://127.0.0.1:3000/static/' + product.images[0]"
height="200"
<card-view
:title="tour.band.name"
:subtitle="tour.name"
:prepend-image="'http://127.0.0.1:3000/static/bands/' + tour.band.images[0]"
link
>
<template #placeholder>
<v-skeleton-loader type="image" />
</template>
</v-img>
</v-sheet>
</v-col>
{{ tour.band.descriptionDe }}
<v-col cols="7" class="pl-0 pt-5">
<div class="text-h6">{{ product.name }}</div>
<div>
<template #actions>
<OutlinedButton>
{{ tour.shows.length }} {{ $t('tours.concert', tour.shows.length) }}
</OutlinedButton>
</template>
<!-- <template> -->
<!-- <div>
<v-rating
size="medium"
:model-value="product.rating"
@@ -40,19 +38,19 @@ defineProps({
/>
{{ product.rating }}/5
</div>
</div> -->
<div>
<!-- <div>
<v-list class="pa-0">
<v-list-item v-for="i in 2" class="pa-0 ma-0">
<v-icon icon="mdi-circle-small" /> {{ product.specs[i - 1] }}
</v-list-item>
</v-list>
</div>
</v-col>
</div> -->
<!-- </v-col> -->
<v-col cols="2" class="pt-5 pr-7">
<div v-if="product.discount == 0" class="font-weight-bold text-h5 text-right">
<!-- <v-col cols="2" class="pt-5 pr-7"> -->
<!-- <div v-if="product.discount == 0" class="font-weight-bold text-h5 text-right">
{{ product.price }}
</div>
@@ -63,9 +61,9 @@ defineProps({
<div>
<div class="text-decoration-line-through text-right">{{ product.price }} </div>
</div>
</div>
</div> -->
<div style="position: absolute; bottom: 0; right: 0;" class="pr-2 pb-2">
<!-- <div style="position: absolute; bottom: 0; right: 0;" class="pr-2 pb-2">
<div v-if="product.inStock > 5" class="text-green-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
@@ -75,10 +73,8 @@ defineProps({
<div v-else class="text-red">
{{ $t("product.soldOut") }}
</div>
</div>
</v-col>
</v-row>
</template>
</div> -->
<!-- </template> -->
</card-view>
</template>

View File

@@ -1,7 +1,7 @@
import AccountPage from "@/pages/accountPage/index.vue";
import OrdersPage from "@/pages/ordersPage/index.vue";
import PreferencesPage from "@/pages/preferencesPage/index.vue";
import ProductsPage from "@/pages/productsPage/index.vue";
import TourPage from "@/pages/toursPage/index.vue";
import LoginPage from "@/pages/loginPage/index.vue"
import BasketPage from "@/pages/basketPage/index.vue"
import HelpPage from "@/pages/helpPage/index.vue"
@@ -9,7 +9,7 @@ import ScoreBoardPage from "@/pages/scoreBoardPage/index.vue"
import adminRoutes from "./admin.routes";
const routes = [
{ path: '/', component: ProductsPage },
{ path: '/', component: TourPage },
{ path: '/account', component: AccountPage },
{ path: '/orders', component: OrdersPage },
{ path: '/preferences', component: PreferencesPage },