Store products in a basket, display list of products in basket
This commit is contained in:
@@ -4,8 +4,10 @@ import axios from 'axios';
|
|||||||
import { useTheme } from 'vuetify/lib/framework.mjs';
|
import { useTheme } from 'vuetify/lib/framework.mjs';
|
||||||
import { useUserStore } from './data/stores/userStore';
|
import { useUserStore } from './data/stores/userStore';
|
||||||
import vuetify from './plugins/vuetify';
|
import vuetify from './plugins/vuetify';
|
||||||
|
import { useBasketStore } from './data/stores/basketStore';
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const basketStore = useBasketStore()
|
||||||
const categories = ref([])
|
const categories = ref([])
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const navRail = ref(vuetify.display.mobile)
|
const navRail = ref(vuetify.display.mobile)
|
||||||
@@ -37,7 +39,13 @@ requestAllCategories()
|
|||||||
<div v-else></div>
|
<div v-else></div>
|
||||||
</v-list-subheader>
|
</v-list-subheader>
|
||||||
<v-list-item title="Produkte" prepend-icon="mdi-store" to="/products" link />
|
<v-list-item title="Produkte" prepend-icon="mdi-store" to="/products" link />
|
||||||
<v-list-item title="Warenkorb" prepend-icon="mdi-cart" to="/basket" link />
|
<v-list-item to="/basket" link title="Warenkorb">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-badge color="primary" :content="basketStore.productsInBasket.length">
|
||||||
|
<v-icon icon="mdi-cart" />
|
||||||
|
</v-badge>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
<v-divider />
|
<v-divider />
|
||||||
|
|
||||||
@@ -61,14 +69,8 @@ requestAllCategories()
|
|||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
|
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-container>
|
<!-- Here changes the router the content -->
|
||||||
<v-row>
|
<router-view></router-view>
|
||||||
<v-col>
|
|
||||||
<!-- Here changes the router the content -->
|
|
||||||
<router-view></router-view>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</v-main>
|
</v-main>
|
||||||
</v-app>
|
</v-app>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export class ProductModel {
|
|||||||
price: number = 0
|
price: number = 0
|
||||||
discount: number = 0
|
discount: number = 0
|
||||||
rating: number = 1
|
rating: number = 1
|
||||||
|
nrOfArticles: number = 2
|
||||||
imageUrl: string = ""
|
imageUrl: string = ""
|
||||||
createdAt: string = ""
|
createdAt: string = ""
|
||||||
updatedAt: string = ""
|
updatedAt: string = ""
|
||||||
|
|||||||
30
software/src/data/stores/basketStore.ts
Normal file
30
software/src/data/stores/basketStore.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
|
import { ProductModel } from "../models/productModel";
|
||||||
|
import { calcProductPrice } from "@/scripts/productScripts";
|
||||||
|
|
||||||
|
export const useBasketStore = defineStore('basket', {
|
||||||
|
state: () => ({
|
||||||
|
productsInBasket: useLocalStorage<Array<ProductModel>>("hackmycart/basketStore/productsInBasket", [])
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
getTotalPrice() {
|
||||||
|
let result = 0
|
||||||
|
|
||||||
|
for (let product of this.productsInBasket) {
|
||||||
|
result += calcProductPrice(product)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
removeProductFromBasket(product: ProductModel) {
|
||||||
|
this.productsInBasket = this.productsInBasket.filter((p: ProductModel) =>
|
||||||
|
p.id != product.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
Products
|
|
||||||
</template>
|
|
||||||
33
software/src/pages/basketPage/basketItem.vue
Normal file
33
software/src/pages/basketPage/basketItem.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ProductModel } from '@/data/models/productModel';
|
||||||
|
import { useBasketStore } from '@/data/stores/basketStore';
|
||||||
|
import { calcProductPrice } from '@/scripts/productScripts';
|
||||||
|
|
||||||
|
const basketStore = useBasketStore()
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
product: {
|
||||||
|
type: ProductModel,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function removeProductFromBasket(product: ProductModel) {
|
||||||
|
basketStore.removeProductFromBasket(product)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-list-item
|
||||||
|
:title="product.name"
|
||||||
|
:subtitle="product.brand"
|
||||||
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-btn icon="mdi-delete" flat @click="removeProductFromBasket(product)"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:append>
|
||||||
|
{{ calcProductPrice(product) }} €
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
35
software/src/pages/basketPage/index.vue
Normal file
35
software/src/pages/basketPage/index.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useBasketStore } from '@/data/stores/basketStore';
|
||||||
|
import basketItem from './basketItem.vue';
|
||||||
|
|
||||||
|
const basketStore = useBasketStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-container max-width="800">
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-card title="Warenkorb" >
|
||||||
|
<v-card-subtitle>
|
||||||
|
{{ basketStore.productsInBasket.length }} Artikel
|
||||||
|
</v-card-subtitle>
|
||||||
|
|
||||||
|
<v-list>
|
||||||
|
<basket-item
|
||||||
|
v-for="product in basketStore.productsInBasket"
|
||||||
|
:product="product"
|
||||||
|
/>
|
||||||
|
</v-list>
|
||||||
|
|
||||||
|
<v-card-text class="text-right">
|
||||||
|
Total: {{ basketStore.getTotalPrice }} €
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions>
|
||||||
|
<v-btn prepend-icon="mdi-basket-check">Bestellen</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
@@ -114,34 +114,36 @@ watch(() => onlyDiscounts.value, () => { filterProducts() })
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-row>
|
<v-container>
|
||||||
<v-col>
|
<v-row>
|
||||||
<filter-bar
|
<v-col>
|
||||||
:number-of-items="filteredProducts.length"
|
<filter-bar
|
||||||
:categories="categories"
|
:number-of-items="filteredProducts.length"
|
||||||
:sort-by="sortBy"
|
:categories="categories"
|
||||||
v-model:selected-category="selectedCategory"
|
:sort-by="sortBy"
|
||||||
v-model:sorted-by="sortedBy"
|
v-model:selected-category="selectedCategory"
|
||||||
v-model:only-discounts="onlyDiscounts"
|
v-model:sorted-by="sortedBy"
|
||||||
/>
|
v-model:only-discounts="onlyDiscounts"
|
||||||
</v-col>
|
/>
|
||||||
</v-row>
|
</v-col>
|
||||||
<v-row dense>
|
</v-row>
|
||||||
<v-col v-if="filteredProducts.length > 0" v-for="product in filteredProducts" cols="6" lg="4" xl="3">
|
<v-row dense>
|
||||||
<product-card
|
<v-col v-if="filteredProducts.length > 0" v-for="product in filteredProducts" cols="12" sm="6" lg="4" xl="3">
|
||||||
:product="product"
|
<product-card
|
||||||
:category="getCategoryById(product.categoryId)"
|
:product="product"
|
||||||
@click="showProductDialog(product)"
|
:category="getCategoryById(product.categoryId)"
|
||||||
/>
|
@click="showProductDialog(product)"
|
||||||
</v-col>
|
/>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
<v-col v-else>
|
<v-col v-else>
|
||||||
<v-empty-state
|
<v-empty-state
|
||||||
icon="mdi-magnify"
|
icon="mdi-magnify"
|
||||||
headline="Keine Artikel gefunden"
|
headline="Keine Artikel gefunden"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
|
||||||
<product-details v-model="showProductDetails" :product="dialogProduct" :productCategory="getCategoryById(dialogProduct.categoryId)" />
|
<product-details v-model="showProductDetails" :product="dialogProduct" :productCategory="getCategoryById(dialogProduct.categoryId)" />
|
||||||
</template>
|
</template>
|
||||||
@@ -3,14 +3,21 @@ import { VNumberInput } from 'vuetify/labs/VNumberInput'
|
|||||||
import { ProductModel } from '@/data/models/productModel';
|
import { ProductModel } from '@/data/models/productModel';
|
||||||
import { CategoryModel } from '@/data/models/categoryModel';
|
import { CategoryModel } from '@/data/models/categoryModel';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { useBasketStore } from '@/data/stores/basketStore';
|
||||||
|
|
||||||
const showDialog = defineModel("showDialog", { type: Boolean })
|
const showDialog = defineModel("showDialog", { type: Boolean })
|
||||||
const nrOfArticles = ref(1)
|
const nrOfArticles = ref(1)
|
||||||
|
const basketStore = useBasketStore()
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
product: ProductModel,
|
product: ProductModel,
|
||||||
productCategory: CategoryModel
|
productCategory: CategoryModel
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function addProductToBasket() {
|
||||||
|
basketStore.productsInBasket.push(props.product)
|
||||||
|
showDialog.value = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -49,7 +56,12 @@ defineProps({
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-btn prepend-icon="mdi-cart-plus" >Zum Einkaufswagen hinzufügen</v-btn>
|
<v-btn
|
||||||
|
prepend-icon="mdi-cart-plus"
|
||||||
|
@click="addProductToBasket"
|
||||||
|
>
|
||||||
|
Zum Einkaufswagen hinzufügen
|
||||||
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import OrdersPage from "@/pages/OrdersPage.vue";
|
|||||||
import PreferencesPage from "@/pages/preferencesPage/index.vue";
|
import PreferencesPage from "@/pages/preferencesPage/index.vue";
|
||||||
import ProductsPage from "@/pages/productsPage/index.vue";
|
import ProductsPage from "@/pages/productsPage/index.vue";
|
||||||
import LoginPage from "@/pages/loginPage/index.vue"
|
import LoginPage from "@/pages/loginPage/index.vue"
|
||||||
|
import BasketPage from "@/pages/basketPage/index.vue"
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ path: '/products', component: ProductsPage },
|
{ path: '/products', component: ProductsPage },
|
||||||
@@ -10,6 +11,7 @@ const routes = [
|
|||||||
{ path: '/orders', component: OrdersPage },
|
{ path: '/orders', component: OrdersPage },
|
||||||
{ path: '/preferences', component: PreferencesPage },
|
{ path: '/preferences', component: PreferencesPage },
|
||||||
{ path: '/login', component: LoginPage },
|
{ path: '/login', component: LoginPage },
|
||||||
|
{ path: '/basket', component: BasketPage }
|
||||||
]
|
]
|
||||||
|
|
||||||
export default routes
|
export default routes
|
||||||
5
software/src/scripts/productScripts.ts
Normal file
5
software/src/scripts/productScripts.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { ProductModel } from "@/data/models/productModel";
|
||||||
|
|
||||||
|
export function calcProductPrice(product: ProductModel): number {
|
||||||
|
return Math.round(product.price * ((100 - product.discount) / 100) * 100) / 100
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user