diff --git a/software/backend/data/products.json b/software/backend/data/products.json index d203121..24bf1a4 100644 --- a/software/backend/data/products.json +++ b/software/backend/data/products.json @@ -52,7 +52,7 @@ "categoryId": 3, "discount": 0, "rating": 4.2, - "description": "", + "description": "Goethe schrieb über 60 Jahre an seinem »Faust« und nannte »diese sehr ernsten Scherze« am Ende sein »Hauptgeschäft«: Dabei entstand eines der großartigsten und gleichzeitig komplexesten Werke der Weltliteratur. Den »Faust« gibt es bei Reclam in vielen Ausgaben, preisniedrig für Schüler, mit Kommentar für Studenten, bibliophil für Liebhaber – jetzt endlich auch eine Doppelausgabe der beiden klassischen Theatertexte in der Universal-Bibliothek: »Faust I« und »II« in einem Band.", "imageUrl": "https://f.media-amazon.com/images/I/71p1k4JwDqL._SL1500_.jpg" }, { @@ -63,7 +63,7 @@ "categoryId": 3, "discount": 0, "rating": 3.5, - "description": "", + "description": " Hauke Haien ist ein genialer Außenseiter, der sich als junger Deichgraf einen Jugendtraum erfüllt: den Bau eines neuartigen Deiches, der den Wellen besser standhalten soll. Die Dorfbewohner sind skeptisch und sehen in ihm die Verkörperung einer uralten Sage: Wenn er auf seinem Schimmel über den Deich reitet, wird Hauke Haien zum dämonischen Reiter, der ihr Leben und ihre Gesetze aus dem Gleichgewicht bringt. Theodor Storms bekannteste Novelle ist ein Meisterwerk realistischer Erzählkunst, in dem es um den Widerstreit von Rationalität und Aberglaube, Fortschritt und Tradition geht.", "imageUrl": "https://f.media-amazon.com/images/I/81uUWtGmKtL._SL1500_.jpg" }, { diff --git a/software/backend/database.ts b/software/backend/database.ts index fbe9012..5f38cdb 100644 --- a/software/backend/database.ts +++ b/software/backend/database.ts @@ -23,7 +23,7 @@ export const sequelize = new Sequelize({ export function startDatabase() { // Create database and tables - sequelize.sync({ force: true }) + sequelize.sync({ force: false }) .then(() => { console.log(`Database & tables created!`) }) diff --git a/software/backend/routes/product.routes.ts b/software/backend/routes/product.routes.ts index 4faaa81..7d42370 100644 --- a/software/backend/routes/product.routes.ts +++ b/software/backend/routes/product.routes.ts @@ -3,10 +3,17 @@ import { Product } from "../models/product.model"; export const product = Router() -product.get("/", (req: Request, res: Response, next: NextFunction)=> { +product.get("/", (req: Request, res: Response, next: NextFunction) => { Product.findAll() .then(products => { res.json(products) }) .catch(next) +}) + +product.get("/:productId", (req: Request, res: Response, next: NextFunction) => { + Product.findByPk(req.params.productId) + .then(product => { + res.json(product) + }) }) \ No newline at end of file diff --git a/software/src/App.vue b/software/src/App.vue index 7b10680..fded46e 100644 --- a/software/src/App.vue +++ b/software/src/App.vue @@ -41,7 +41,7 @@ requestAllCategories() diff --git a/software/src/data/models/basketItemModel.ts b/software/src/data/models/basketItemModel.ts new file mode 100644 index 0000000..d3fcda3 --- /dev/null +++ b/software/src/data/models/basketItemModel.ts @@ -0,0 +1,10 @@ +export class BasketItemModel { + productId: number = -1 + brand: string = "" + name: string = "" + categoryName: string = "" + categoryIcon: string = "" + price: number = 0 + discount: number = 0 + quantity: number = 1 +} \ No newline at end of file diff --git a/software/src/data/models/orderedItemModel.ts b/software/src/data/models/orderedItemModel.ts new file mode 100644 index 0000000..bf9e4f3 --- /dev/null +++ b/software/src/data/models/orderedItemModel.ts @@ -0,0 +1,6 @@ +export class OrderedItemModel { + orderId: number = -1 + productId: number = -1 + quantity: number = 1 + totalPrice: number = 0 +} \ No newline at end of file diff --git a/software/src/data/models/productModel.ts b/software/src/data/models/productModel.ts index 96596c1..7f266b9 100644 --- a/software/src/data/models/productModel.ts +++ b/software/src/data/models/productModel.ts @@ -7,7 +7,6 @@ export class ProductModel { price: number = 0 discount: number = 0 rating: number = 1 - nrOfArticles: number = 2 imageUrl: string = "" createdAt: string = "" updatedAt: string = "" diff --git a/software/src/data/stores/basketStore.ts b/software/src/data/stores/basketStore.ts index bd510c9..f9981ac 100644 --- a/software/src/data/stores/basketStore.ts +++ b/software/src/data/stores/basketStore.ts @@ -1,30 +1,39 @@ import { defineStore } from "pinia"; import { useLocalStorage } from "@vueuse/core"; -import { ProductModel } from "../models/productModel"; import { calcProductPrice } from "@/scripts/productScripts"; +import { BasketItemModel } from "../models/basketItemModel"; export const useBasketStore = defineStore('basket', { state: () => ({ - productsInBasket: useLocalStorage>("hackmycart/basketStore/productsInBasket", []) + itemsInBasket: useLocalStorage>("hackmycart/basketStore/productsInBasket", []) }), getters: { getTotalPrice() { let result = 0 - for (let product of this.productsInBasket) { - result += calcProductPrice(product) + for (let item of this.itemsInBasket) { + result += calcProductPrice(item, item.quantity) } - return result + return Math.round(result * 100) / 100 } }, actions: { - removeProductFromBasket(product: ProductModel) { - this.productsInBasket = this.productsInBasket.filter((p: ProductModel) => - p.id != product.id + removeItemFromBasket(item: BasketItemModel) { + this.itemsInBasket = this.itemsInBasket.filter((basketItemModel: BasketItemModel) => + basketItemModel.productId != item.productId ) + }, + + addItemToBasket(item: BasketItemModel) { + // Product is already in the basket, increase number of items + if (this.itemsInBasket.find((basketItem) => basketItem.productId == item.productId)) { + this.itemsInBasket.find((basketItem) => basketItem.productId == item.productId).quantity += item.quantity + } else { + this.itemsInBasket.push(item) + } } } }) diff --git a/software/src/pages/basketPage/basketItem.vue b/software/src/pages/basketPage/basketItem.vue deleted file mode 100644 index 22ffb5c..0000000 --- a/software/src/pages/basketPage/basketItem.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - \ No newline at end of file diff --git a/software/src/pages/basketPage/index.vue b/software/src/pages/basketPage/index.vue index d8e9072..94e7057 100644 --- a/software/src/pages/basketPage/index.vue +++ b/software/src/pages/basketPage/index.vue @@ -1,32 +1,38 @@ \ No newline at end of file diff --git a/software/src/pages/productsPage/productDetails.vue b/software/src/pages/productsPage/productDetails.vue index 9cd06f1..c110fed 100644 --- a/software/src/pages/productsPage/productDetails.vue +++ b/software/src/pages/productsPage/productDetails.vue @@ -2,10 +2,11 @@ import { VNumberInput } from 'vuetify/labs/VNumberInput' import { ProductModel } from '@/data/models/productModel'; import { CategoryModel } from '@/data/models/categoryModel'; -import { ref } from 'vue'; +import { ModelRef, ref } from 'vue'; import { useBasketStore } from '@/data/stores/basketStore'; +import { calcProductPrice, productToBasketItem } from '@/scripts/productScripts'; -const showDialog = defineModel("showDialog", { type: Boolean }) +const showDialog: ModelRef = defineModel() const nrOfArticles = ref(1) const basketStore = useBasketStore() @@ -15,7 +16,8 @@ const props = defineProps({ }) function addProductToBasket() { - basketStore.productsInBasket.push(props.product) + basketStore.addItemToBasket(productToBasketItem(props.product, props.productCategory, nrOfArticles.value)) + nrOfArticles.value = 1 showDialog.value = false } @@ -46,11 +48,16 @@ function addProductToBasket() { :hideInput="false" :inset="false" v-model="nrOfArticles" + :min="1" + :max="10" + density="comfortable" /> - - {{ nrOfArticles * product.price }} € + + + + {{ calcProductPrice(product, nrOfArticles) }} € diff --git a/software/src/scripts/productScripts.ts b/software/src/scripts/productScripts.ts index ada7c91..51701a4 100644 --- a/software/src/scripts/productScripts.ts +++ b/software/src/scripts/productScripts.ts @@ -1,5 +1,44 @@ +import { BasketItemModel } from "@/data/models/basketItemModel"; +import { CategoryModel } from "@/data/models/categoryModel"; import { ProductModel } from "@/data/models/productModel"; -export function calcProductPrice(product: ProductModel): number { - return Math.round(product.price * ((100 - product.discount) / 100) * 100) / 100 -} \ No newline at end of file +export function calcProductPrice(product: ProductModel, quantity: number = 1): number { + return calcPrice(product.price, product.discount, quantity) +} + +/** + * 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 +} + +/** + * Convert a ProductModel to a BasketModel + * + * @param product ProductModel to convert + * @param productCategory Category of the product + * @param quantity Number of units + * + * @returns BasketItemModel + */ +export function productToBasketItem(product: ProductModel, productCategory: CategoryModel, quantity: number): BasketItemModel { + let result = new BasketItemModel() + + result.productId = product.id + result.quantity = quantity + result.price = product.price + result.brand = product.brand + result.discount = product.discount + result.name = product.name + result.categoryName = productCategory.name + result.categoryIcon = productCategory.icon + + return result +} \ No newline at end of file