Connect Orders database table with Payments and Addresses, visualize it in the frontend

This commit is contained in:
2024-09-24 23:41:35 +02:00
parent e00107ab6a
commit cbd01f6d59
19 changed files with 517 additions and 223 deletions

View File

@@ -32,13 +32,15 @@ function confirmPressed() {
<template #actions>
<outlined-button
@click="showDialog = false"
color="green"
prepend-icon="mdi-close"
color="orange"
>
{{ $t("dialog.cancel") }}
</outlined-button>
<outlined-button
@click="confirmPressed"
prepend-icon="mdi-check"
color="red"
>
{{ $t("dialog.confirm") }}

View File

@@ -9,7 +9,12 @@ export async function getUserOrders(userId: number) {
return axios.get(BASE_URL + "/" + userId)
}
export async function addOrder(accountId: number, basketItems: Array<BasketItemModel>) {
export async function addOrder(
accountId: number,
basketItems: Array<BasketItemModel>,
paymentId: number,
addressId: number
) {
let orderItems = []
for (let basketItem of basketItems) {
@@ -22,6 +27,8 @@ export async function addOrder(accountId: number, basketItems: Array<BasketItemM
return axios.post(BASE_URL, {
accountId: accountId,
orderItems: orderItems
orderItems: orderItems,
paymentId: paymentId,
addressId: addressId
})
}

View File

@@ -1,4 +1,6 @@
import { AddressModel } from "./addressModel"
import { OrderItemModel } from "./orderItemModel"
import { PaymentModel } from "./paymentModel"
export class OrderModel {
id: number
@@ -6,4 +8,6 @@ export class OrderModel {
shippingProgress: number
orderItems: Array<OrderItemModel>
orderedAt: string
payment: PaymentModel
address: AddressModel
}

View File

@@ -8,10 +8,14 @@ import { addOrder } from "../api/orderApi";
import { useAccountStore } from "./accountStore";
import { ProductModel } from "../models/productModel";
import { useProductStore } from "./productStore";
import { AddressModel } from "../models/addressModel";
import { PaymentModel } from "../models/paymentModel";
export const useBasketStore = defineStore('basketStore', {
state: () => ({
itemsInBasket: useLocalStorage<Array<BasketItemModel>>("hackmycart/basketStore/productsInBasket", [])
itemsInBasket: useLocalStorage<Array<BasketItemModel>>("hackmycart/basketStore/productsInBasket", []),
usedAddress: useLocalStorage("hackmycart/basketStore/usedAddress", new AddressModel()),
usedPayment: useLocalStorage("hackmycart/basketStore/usedPayment", new PaymentModel())
}),
getters: {
@@ -73,12 +77,20 @@ export const useBasketStore = defineStore('basketStore', {
async takeOrder() {
const accountStore = useAccountStore()
const productStore = useProductStore()
const feedbackStore = useFeedbackStore()
await addOrder(accountStore.userAccount.id, this.itemsInBasket)
this.itemsInBasket = []
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 accountStore.refreshOrders()
await productStore.fetchAllProducts()
this.itemsInBasket = []
feedbackStore.changeBanner(BannerStateEnum.ORDERPLACESUCCESSFUL)
} else {
feedbackStore.changeBanner(BannerStateEnum.ERROR)
}
})
}
}
})

View File

@@ -20,6 +20,7 @@ export const useFeedbackStore = defineStore("feedbackStore", {
actions: {
changeBanner(bannerState: BannerStateEnum) {
// Banner message
switch (bannerState) {
////////// System feedback //////////
@@ -107,6 +108,8 @@ export const useFeedbackStore = defineStore("feedbackStore", {
}
}
// Banner color
switch (bannerState) {
case BannerStateEnum.ERROR:
case BannerStateEnum.ACCOUNTLOGINERROR:
@@ -118,9 +121,9 @@ export const useFeedbackStore = defineStore("feedbackStore", {
case BannerStateEnum.PRODUCTDELETESUCCESSFUL:
case BannerStateEnum.PRODUCTDELETEERROR:
this.color = "red"
this.icon = "mdi-alert-circle"
break;
case BannerStateEnum.BASKETPRODUCTADDED:
case BannerStateEnum.DATABASERESETSUCCESSFUL:
case BannerStateEnum.ACCOUNTLOGINSUCCESSFUL:
case BannerStateEnum.ACCOUNTREGISTERSUCCESSFUL:
@@ -132,10 +135,74 @@ export const useFeedbackStore = defineStore("feedbackStore", {
case BannerStateEnum.PRODUCTCREATESUCCESSFUL:
case BannerStateEnum.PRODUCTCREATEERROR:
this.color = "green"
this.icon = "mdi-check-circle"
break
break;
case BannerStateEnum.BASKETPRODUCTREMOVED:
this.color = "blue"
}
// Banner icon
switch (bannerState) {
case BannerStateEnum.ERROR:
this.icon = "mdi-alert-circle"
break;
case BannerStateEnum.ACCOUNTLOGINERROR:
case BannerStateEnum.ACCOUNTLOGINWRONGLOGIN:
case BannerStateEnum.ACCOUNTREGISTERERROR:
case BannerStateEnum.ACCOUNTREGISTERUSERNAMEINUSE:
this.icon = "mdi-account"
break;
case BannerStateEnum.CATEGORYCREATEERROR:
case BannerStateEnum.CATEGORYDELETEERROR:
case BannerStateEnum.CATEGORYCREATESUCCESSFUL:
case BannerStateEnum.CATEGORYDELETESUCESSFUL:
this.icon = "mdi-label"
break;
case BannerStateEnum.PRODUCTDELETESUCCESSFUL:
case BannerStateEnum.PRODUCTDELETEERROR:
case BannerStateEnum.PRODUCTCREATESUCCESSFUL:
case BannerStateEnum.PRODUCTCREATEERROR:
this.icon = "mdi-store"
break;
case BannerStateEnum.DATABASERESETSUCCESSFUL:
this.icon = "mdi-database-refresh"
break;
case BannerStateEnum.BASKETPRODUCTADDED:
case BannerStateEnum.BASKETPRODUCTREMOVED:
this.icon = "mdi-basket"
break;
case BannerStateEnum.ORDERPLACESUCCESSFUL:
this.icon = "mdi-basket-check"
break;
case BannerStateEnum.ACCOUNTLOGOUTSUCCESSFUL:
this.icon = "mdi-logout"
break;
case BannerStateEnum.ACCOUNTLOGINSUCCESSFUL:
this.icon = "mdi-login"
break;
case BannerStateEnum.ACCOUNTREGISTERSUCCESSFUL:
this.icon = "mdi-account-plus"
break;
case BannerStateEnum.ACCOUNTUPDATESUCCESSFUL:
this.icon = "mdi-account-reactivate"
break;
}
this.showBanner = true
}
}

View File

@@ -66,8 +66,8 @@
"backToLogin": "Zurück zum Login",
"delete": "Account löschen",
"managingAccount": "Account verwalten",
"addresses": "Adressen",
"payments": "Bezahlarten",
"address": "Adresse | Addressen",
"payment": "Bezahlart | Bezahlarten",
"masterData": "Stammdaten",
"noAddresses": "Keine Adressen gefunden",
"noPayments": "Keine Bezahlarten gefunden",

View File

@@ -66,8 +66,8 @@
"register": "Create Account",
"delete": "Delete Account",
"managingAccount": "Managing Account",
"addresses": "Addresses",
"payments": "Payments",
"address": "Address | Addresses",
"payment": "Payment | Payments",
"masterData": "Master data",
"noAddresses": "No Addresses found",
"noPayments": "No payments found",

View File

@@ -9,12 +9,28 @@ const basketStore = useBasketStore()
const accountStore = useAccountStore()
const showDialog: ModelRef<boolean> = defineModel()
const orderingInProgress = ref(false)
const addressError = ref(false)
const paymentError = ref(false)
async function doOrder() {
orderingInProgress.value = true
await basketStore.takeOrder()
addressError.value = false
paymentError.value = false
if (basketStore.usedAddress == null) {
addressError.value = true
}
if (basketStore.usedPayment == null){
paymentError.value = true
}
if (basketStore.usedAddress != null && basketStore.usedPayment != null) {
await basketStore.takeOrder()
showDialog.value = false
}
showDialog.value = false
orderingInProgress.value = false
}
</script>
@@ -25,29 +41,65 @@ async function doOrder() {
icon="mdi-basket-check"
v-model="showDialog"
max-width="800"
persistent
>
<v-row>
<v-col>
Address
{{ $t('account.address', accountStore.userAccount.addresses.length) }}
</v-col>
</v-row>
<v-row>
<v-col>
<v-radio-group>
<v-radio-group
v-model="basketStore.usedAddress"
:error="addressError"
>
<v-radio
v-for="address in accountStore.userAccount.addresses"
:value="address"
:label="address.street + ' ' + address.houseNumber + ', ' + address.postalCode + ' ' + address.city"
/>
</v-radio-group>
</v-col>
</v-row>
<v-row>
<v-col>
{{ $t('account.payment', accountStore.userAccount.payments.length) }}
</v-col>
</v-row>
<v-row>
<v-col>
<v-radio-group
v-model="basketStore.usedPayment"
>
<v-radio
v-for="payment in accountStore.userAccount.payments"
:value="payment"
:label="payment.bankName + ': ' + payment.iban"
:error="paymentError"
/>
</v-radio-group>
</v-col>
</v-row>
<template #actions>
<outlined-button
@click="showDialog = false"
prepend-icon="mdi-close"
color="orange"
>
{{ $t('dialog.cancel') }}
</outlined-button>
<outlined-button
@click="doOrder"
:loading="orderingInProgress"
prepend-icon="mdi-send"
color="green"
>
{{ $t('ordering.takeOrder') }}
</outlined-button>

View File

@@ -36,46 +36,81 @@ function formatDateTimeString(string: string) {
:subtitle="$t('totalPrice') + ': ' + accountStore.getOrderTotalPrice(order.id) + ' €'"
>
<template #withoutContainer>
<v-timeline direction="horizontal" side="start" size="x-large">
<v-timeline-item :dot-color="getDotColor(order, 1)" icon="mdi-basket-check">
{{ $t('orders.ordered') }}
</v-timeline-item>
<v-row>
<v-col>
<v-timeline direction="horizontal" side="start" size="x-large">
<v-timeline-item :dot-color="getDotColor(order, 1)" icon="mdi-basket-check">
{{ $t('orders.ordered') }}
</v-timeline-item>
<v-timeline-item :dot-color="getDotColor(order, 2)" icon="mdi-package-variant">
{{ $t('orders.preparingForShipping') }}
</v-timeline-item>
<v-timeline-item :dot-color="getDotColor(order, 2)" icon="mdi-package-variant">
{{ $t('orders.preparingForShipping') }}
</v-timeline-item>
<v-timeline-item :dot-color="getDotColor(order, 3)" icon="mdi-send">
{{ $t('orders.shipped') }}
</v-timeline-item>
<v-timeline-item :dot-color="getDotColor(order, 3)" icon="mdi-send">
{{ $t('orders.shipped') }}
</v-timeline-item>
<v-timeline-item :dot-color="getDotColor(order, 4)" icon="mdi-truck-fast">
{{ $t('orders.inDelivery') }}
</v-timeline-item>
<v-timeline-item :dot-color="getDotColor(order, 4)" icon="mdi-truck-fast">
{{ $t('orders.inDelivery') }}
</v-timeline-item>
<v-timeline-item :dot-color="getDotColor(order, 5)" icon="mdi-package-check">
{{ $t('orders.delivered') }}
</v-timeline-item>
</v-timeline>
<v-timeline-item :dot-color="getDotColor(order, 5)" icon="mdi-package-check">
{{ $t('orders.delivered') }}
</v-timeline-item>
</v-timeline>
</v-col>
</v-row>
<v-table class="bg-surface-light">
<thead>
<tr>
<th>{{ $t('quantity') }}</th>
<th>{{ $t('product.brand') }}</th>
<th>{{ $t('product.productName') }}</th>
<th>{{ $t('product.productPrice') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="orderItem in order.orderItems">
<td>{{ orderItem.quantity }}x</td>
<td>{{ orderItem.product.brand.name }}</td>
<td>{{ orderItem.product.name }}</td>
<td>{{ orderItem.product.price }} </td>
</tr>
</tbody>
</v-table>
<v-row>
<v-col>
<v-card variant="outlined" class="ml-5 pa-3">
<div class="text-h6">
<v-icon icon="mdi-home" />
{{ $t('account.address', 1) }}
</div>
<div class="pl-9">{{ order.address.street }} {{ order.address.houseNumber }}</div>
<div class="pl-9">{{ order.address.postalCode }} {{ order.address.city }}</div>
</v-card>
</v-col>
<v-col>
<v-card variant="outlined" class="mr-5 pa-3">
<div class="text-h6">
<v-icon icon="mdi-currency-usd" />
{{ $t('account.payment', 1) }}
</div>
<div class="pl-9">{{ order.payment.bankName }}</div>
<div class="pl-9">{{ order.payment.iban }}</div>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col>
<v-table class="bg-surface-light">
<thead>
<tr>
<th>{{ $t('quantity') }}</th>
<th>{{ $t('product.brand') }}</th>
<th>{{ $t('product.productName') }}</th>
<th>{{ $t('product.productPrice') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="orderItem in order.orderItems">
<td>{{ orderItem.quantity }}x</td>
<td>{{ orderItem.product.brand.name }}</td>
<td>{{ orderItem.product.name }}</td>
<td>{{ orderItem.product.price }} </td>
</tr>
</tbody>
</v-table>
</v-col>
</v-row>
</template>
</card-view>
</template>

View File

@@ -142,7 +142,7 @@ watch(() => props.product.images, () => {
v-model="nrOfArticles"
variant="outlined"
:min="1"
:max="10"
:max="product.inStock"
density="comfortable"
:hide-details="true"
:disabled="product.inStock == 0"