Multiple addresses & payments of an account configurable in frontend

This commit is contained in:
2024-09-24 13:55:48 +02:00
parent 0b34174ce9
commit 22d3e8d177
16 changed files with 460 additions and 370 deletions

View File

@@ -16,16 +16,19 @@ defineProps({
:title="title"
:subtitle="subtitle"
:prepend-icon="icon"
class="card-outter"
>
<slot></slot>
<!-- 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>
<style>
</style>

View File

@@ -23,13 +23,11 @@ function confirmPressed() {
max-width="400"
v-model="showDialog"
>
<v-container>
<v-row>
<v-col>
{{ description }}
</v-col>
</v-row>
</v-container>
<v-row>
<v-col>
{{ description }}
</v-col>
</v-row>
<template #actions>
<outlined-button

View File

@@ -66,7 +66,10 @@
"register": "Account erstellen",
"backToLogin": "Zurück zum Login",
"delete": "Account löschen",
"managingAccount": "Account verwalten"
"managingAccount": "Account verwalten",
"addresses": "Adressen",
"payments": "Bezahlarten",
"masterData": "Stammdaten"
},
"bannerMessages": {
"loginSuccessful": "Login erfolgreich!",

View File

@@ -66,7 +66,10 @@
"noAccountRegister": "Create new Account!",
"register": "Create Account",
"delete": "Delete Account",
"managingAccount": "Managing Account"
"managingAccount": "Managing Account",
"addresses": "Addresses",
"payments": "Payments",
"masterData": "Master data"
},
"bannerMessages": {
"loginSuccessful": "Login erfolgreich!",

View File

@@ -7,85 +7,41 @@ const accountStore = useAccountStore()
</script>
<template>
<card-view title="Account">
<v-container>
<v-row>
<v-col>
<v-text-field
:label="$t('account.username')"
v-model="accountStore.userAccount.username"
disabled
/>
</v-col>
<v-col>
<v-text-field
:label="$t('account.password')"
v-model="accountStore.userAccount.password"
type="password"
/>
</v-col>
</v-row>
<card-view
:title="$t('account.masterData')"
icon="mdi-account"
>
<v-row>
<v-col>
<v-text-field
:label="$t('account.username')"
v-model="accountStore.userAccount.username"
disabled
/>
</v-col>
<v-col>
<v-text-field
:label="$t('account.password')"
v-model="accountStore.userAccount.password"
type="password"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.firstName')"
v-model="accountStore.userAccount.firstName"
/>
</v-col>
<v-col>
<v-text-field
:label="$t('userInfo.lastName')"
v-model="accountStore.userAccount.lastName"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.street')"
v-model="accountStore.userAccount.addresses[0].street"
/>
</v-col>
<v-col>
<v-text-field
:label="$t('userInfo.houseNumber')"
v-model="accountStore.userAccount.addresses[0].houseNumber"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.postalCode')"
v-model="accountStore.userAccount.addresses[0].postalCode"
/>
</v-col>
<v-col>
<v-text-field
:label="$t('userInfo.city')"
v-model="accountStore.userAccount.addresses[0].city"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.bankName')"
v-model="accountStore.userAccount.payments[0].bankName"
/>
</v-col>
<v-col>
<v-text-field
:label="$t('userInfo.iban')"
v-model="accountStore.userAccount.payments[0].iban"
/>
</v-col>
</v-row>
</v-container>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.firstName')"
v-model="accountStore.userAccount.firstName"
/>
</v-col>
<v-col>
<v-text-field
:label="$t('userInfo.lastName')"
v-model="accountStore.userAccount.lastName"
/>
</v-col>
</v-row>
<template #actions>
<outlined-button

View File

@@ -12,20 +12,21 @@ function deleteAccount() {
</script>
<template>
<card-view :title="$t('account.managingAccount')">
<v-container>
<v-row>
<v-col class="d-flex justify-center align-center">
<outlined-button
prepend-icon="mdi-delete"
color="red"
@click="showConfirmDialog = true"
>
{{ $t("account.delete") }}
</outlined-button>
</v-col>
</v-row>
</v-container>
<card-view
:title="$t('account.managingAccount')"
icon="mdi-account-edit"
>
<v-row>
<v-col class="d-flex justify-center align-center">
<outlined-button
prepend-icon="mdi-delete"
color="red"
@click="showConfirmDialog = true"
>
{{ $t("account.delete") }}
</outlined-button>
</v-col>
</v-row>
</card-view>
<confirm-dialog

View File

@@ -0,0 +1,64 @@
<script setup lang="ts">
import cardView from '@/components/cardView.vue';
import { useAccountStore } from '@/data/stores/accountStore';
import outlinedButton from '@/components/outlinedButton.vue';
const accountStore = useAccountStore()
</script>
<template>
<card-view
icon="mdi-home"
:title="$t('account.addresses')"
>
<v-expansion-panels>
<v-expansion-panel
v-for="address in accountStore.userAccount.addresses"
:title="address.street + ' ' + address.houseNumber"
color="grey"
>
<template #text>
<v-row class="pt-5">
<v-col>
<v-text-field
:label="$t('userInfo.street')"
v-model="address.street"
/>
</v-col>
<v-col>
<v-text-field
:label="$t('userInfo.houseNumber')"
v-model="address.houseNumber"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.postalCode')"
v-model="address.postalCode"
/>
</v-col>
<v-col>
<v-text-field
:label="$t('userInfo.city')"
v-model="address.city"
/>
</v-col>
</v-row>
</template>
</v-expansion-panel>
</v-expansion-panels>
<template #actions>
<outlined-button
@click="accountStore.updateAccount()"
prepend-icon="mdi-content-save"
color="green"
>
Save
</outlined-button>
</template>
</card-view>
</template>

View File

@@ -2,6 +2,8 @@
import alertBanner from '@/components/alertBanner.vue';
import accountDataCard from './accountDataCard.vue';
import accountManagingCard from './accountManagingCard.vue';
import addressesCard from './addressesCard.vue';
import paymentsCard from './paymentsCard.vue';
</script>
<template>
@@ -18,6 +20,18 @@ import accountManagingCard from './accountManagingCard.vue';
</v-col>
</v-row>
<v-row>
<v-col>
<addresses-card />
</v-col>
</v-row>
<v-row>
<v-col>
<payments-card />
</v-col>
</v-row>
<v-row>
<v-col>
<account-managing-card />

View File

@@ -0,0 +1,49 @@
<script setup lang="ts">
import cardView from '@/components/cardView.vue';
import { useAccountStore } from '@/data/stores/accountStore';
import outlinedButton from '@/components/outlinedButton.vue';
const accountStore = useAccountStore()
</script>
<template>
<card-view
icon="mdi-currency-usd"
:title="$t('account.payments')"
>
<v-expansion-panels>
<v-expansion-panel
v-for="payment in accountStore.userAccount.payments"
:title="payment.bankName"
color="grey"
>
<template #text>
<v-row class="pt-5">
<v-col>
<v-text-field
:label="$t('userInfo.bankName')"
v-model="payment.bankName"
/>
</v-col>
<v-col>
<v-text-field
:label="$t('userInfo.iban')"
v-model="payment.iban"
/>
</v-col>
</v-row>
</template>
</v-expansion-panel>
</v-expansion-panels>
<template #actions>
<outlined-button
@click="accountStore.updateAccount()"
prepend-icon="mdi-content-save"
color="green"
>
Save
</outlined-button>
</template>
</card-view>
</template>

View File

@@ -16,20 +16,18 @@ function startLogin() {
<template>
<card-view :title="$t('menu.login')" prepend-icon="mdi-login" elevation="8">
<v-container>
<v-row>
<v-col>
<v-text-field :label="$t('account.username')" prepend-icon="mdi-account" clearable v-model="username"/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field :label="$t('account.username')" prepend-icon="mdi-account" clearable v-model="username"/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field :label="$t('account.password')" prepend-icon="mdi-key" type="password"
clearable v-model="password" />
</v-col>
</v-row>
</v-container>
<v-row>
<v-col>
<v-text-field :label="$t('account.password')" prepend-icon="mdi-key" type="password"
clearable v-model="password" />
</v-col>
</v-row>
<template #actions>
<outlined-button

View File

@@ -12,71 +12,69 @@ const accountStore = useAccountStore()
<template>
<card-view :title="$t('account.register')">
<v-container>
<v-row>
<v-col>
<v-text-field
:label="$t('account.username')"
prepend-icon="mdi-account"
v-model="newUser.username"
clearable
/>
</v-col>
<v-row>
<v-col>
<v-text-field
:label="$t('account.username')"
prepend-icon="mdi-account"
v-model="newUser.username"
clearable
/>
</v-col>
<v-col>
<v-text-field
:label="$t('account.password')"
prepend-icon="mdi-key"
type="password"
v-model="newUser.password"
clearable
/>
</v-col>
</v-row>
<v-col>
<v-text-field
:label="$t('account.password')"
prepend-icon="mdi-key"
type="password"
v-model="newUser.password"
clearable
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.firstName')"
prepend-icon="mdi-card-account-details"
v-model="newUser.firstName"
clearable
/>
</v-col>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.firstName')"
prepend-icon="mdi-card-account-details"
v-model="newUser.firstName"
clearable
/>
</v-col>
<v-col>
<v-text-field :label="$t('userInfo.lastName')" v-model="newUser.lastName" clearable />
</v-col>
</v-row>
<v-col>
<v-text-field :label="$t('userInfo.lastName')" v-model="newUser.lastName" clearable />
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.street')"
prepend-icon="mdi-numeric"
v-model="newUser.addresses[0].street"
clearable
/>
</v-col>
<v-col cols="4">
<v-text-field :label="$t('userInfo.houseNumber')" v-model="newUser.addresses[0].houseNumber" clearable />
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('userInfo.street')"
prepend-icon="mdi-numeric"
v-model="newUser.addresses[0].street"
clearable
/>
</v-col>
<v-col cols="4">
<v-text-field :label="$t('userInfo.houseNumber')" v-model="newUser.addresses[0].houseNumber" clearable />
</v-col>
</v-row>
<v-row>
<v-col cols="4">
<v-text-field
:label="$t('userInfo.postalCode')"
prepend-icon="mdi-city"
v-model="newUser.addresses[0].postalCode"
clearable
/>
</v-col>
<v-col>
<v-text-field :label="$t('userInfo.city')" v-model="newUser.addresses[0].city" clearable />
</v-col>
</v-row>
</v-container>
<v-row>
<v-col cols="4">
<v-text-field
:label="$t('userInfo.postalCode')"
prepend-icon="mdi-city"
v-model="newUser.addresses[0].postalCode"
clearable
/>
</v-col>
<v-col>
<v-text-field :label="$t('userInfo.city')" v-model="newUser.addresses[0].city" clearable />
</v-col>
</v-row>
<template #actions>
<outlined-button

View File

@@ -20,25 +20,23 @@ function changeLanguage() {
<template>
<card-view :title="$t('preferences.pageSetup')" prepend-icon="mdi-view-dashboard" elevation="8">
<v-container>
<v-row>
<v-col>
<v-select
v-model="preferencesStore.theme"
:items="themeEnums"
:label="$t('preferences.selectedTheme')"
@update:model-value="changeTheme"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-select v-model="preferencesStore.language" :items="$i18n.availableLocales" :label="$t('preferences.language')"
@update:model-value="changeLanguage"
<v-row>
<v-col>
<v-select
v-model="preferencesStore.theme"
:items="themeEnums"
:label="$t('preferences.selectedTheme')"
@update:model-value="changeTheme"
/>
</v-col>
</v-row>
</v-container>
</v-col>
</v-row>
<v-row>
<v-col>
<v-select v-model="preferencesStore.language" :items="$i18n.availableLocales" :label="$t('preferences.language')"
@update:model-value="changeLanguage"
/>
</v-col>
</v-row>
</card-view>
</template>

View File

@@ -29,27 +29,25 @@ function resetSettings() {
<template>
<card-view :title="$t('preferences.systemSetup')" prepend-icon="mdi-engine" elevation="8">
<v-container>
<v-row>
<v-col class="d-flex justify-center align-center">
<outlined-button
@click="showConfirmDialog = true"
prepend-icon="mdi-database-refresh"
color="red"
>
{{ $t('preferences.resetDatabase') }}
</outlined-button>
</v-col>
<v-col class="d-flex justify-center align-center">
<outlined-button
@click="resetSettings"
prepend-icon="mdi-cog-counterclockwise"
>
{{ $t('preferences.resetPreferences') }}
</outlined-button>
</v-col>
</v-row>
</v-container>
<v-row>
<v-col class="d-flex justify-center align-center">
<outlined-button
@click="showConfirmDialog = true"
prepend-icon="mdi-database-refresh"
color="red"
>
{{ $t('preferences.resetDatabase') }}
</outlined-button>
</v-col>
<v-col class="d-flex justify-center align-center">
<outlined-button
@click="resetSettings"
prepend-icon="mdi-cog-counterclockwise"
>
{{ $t('preferences.resetPreferences') }}
</outlined-button>
</v-col>
</v-row>
</card-view>
<confirm-dialog

View File

@@ -12,71 +12,73 @@ defineProps({
<template>
<card-view link>
<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"
>
<template #placeholder>
<v-skeleton-loader type="image" />
</template>
</v-img>
</v-sheet>
</v-col>
<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"
>
<template #placeholder>
<v-skeleton-loader type="image" />
</template>
</v-img>
</v-sheet>
</v-col>
<v-col cols="7" class="pl-0 pt-5">
<div class="text-h6">{{ product.name }}</div>
<div>
<v-rating
size="medium"
:model-value="product.rating"
active-color="yellow-darken-1"
color="grey-darken-1"
half-increments
readonly
/>
{{ product.rating }}/5
</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>
<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>
<div v-else>
<div class="font-weight-bold text-h5 text-right">
{{ (product.price * ( 100 - product.discount) / 100).toFixed(2) }}
</div>
<v-col cols="7" class="pl-0 pt-5">
<div class="text-h6">{{ product.name }}</div>
<div>
<div class="text-decoration-line-through text-right">{{ product.price }} </div>
</div>
</div>
<v-rating
size="medium"
:model-value="product.rating"
active-color="yellow-darken-1"
color="grey-darken-1"
half-increments
readonly
/>
<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]) }}
{{ product.rating }}/5
</div>
<div v-else-if="product.inStock > 0" class="text-orange-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
<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>
<div v-else class="text-red">
{{ $t("product.soldOut") }}
</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">
{{ product.price }}
</div>
</div>
</v-col>
</v-row>
<div v-else>
<div class="font-weight-bold text-h5 text-right">
{{ (product.price * ( 100 - product.discount) / 100).toFixed(2) }}
</div>
<div>
<div class="text-decoration-line-through text-right">{{ product.price }} </div>
</div>
</div>
<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>
<div v-else-if="product.inStock > 0" class="text-orange-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else class="text-red">
{{ $t("product.soldOut") }}
</div>
</div>
</v-col>
</v-row>
</template>
</card-view>
</template>

View File

@@ -37,101 +37,99 @@ watch(() => props.product.images, () => {
:subtitle="product.category.name"
v-model="showDialog"
>
<v-container class="pt-n3">
<v-row>
<!-- Image col -->
<v-col>
<v-row>
<!-- Image col -->
<v-col>
<v-row>
<v-col class="py-0">
<v-sheet color="white">
<v-img :src="selectedImage" height="600" />
</v-sheet>
</v-col>
</v-row>
<v-row>
<v-spacer />
<v-col v-for="image in product.images">
<v-card
width="60"
@click="selectedImage = 'http://localhost:3000/static/' + image"
color="white"
>
<v-img :src="'http://localhost:3000/static/' + image" height="60" />
</v-card>
</v-col>
<v-spacer />
</v-row>
</v-col>
<!-- Product description col -->
<v-col>
<v-row>
<v-col class="text-h6 pt-0">
{{ $t("product.description") }}
</v-col>
</v-row>
<v-row class="text-body-1 mt-0" style="height: 450px; overflow-y: auto;">
<div v-if="product.description.length != 0">
<v-col class="text-body-1">
{{ product.description }}
</v-col>
</div>
<div v-if="product.specs.length > 1">
<v-list>
<v-list-item v-for="spec in product.specs">
<v-icon icon="mdi-circle-small" /> {{ spec }}
</v-list-item>
</v-list>
</div>
</v-row>
<v-divider class="my-4" />
<v-row>
<v-col class="pb-0">
<div class="d-flex align-center flex-column">
<div class="text-h3"> {{ product.rating }} <span class="text-h6 ml-n3">/5</span> </div>
<v-rating :model-value="product.rating" color="yellow-darken-3" half-increments disabled />
</div>
</v-col>
</v-row>
<v-divider class="my-4" />
<v-row>
<v-col cols="3">
<div class="pt-3">
<div v-if="product.inStock > 5" class="text-green-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else-if="product.inStock > 0" class="text-orange-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else class="text-red">
{{ $t("product.soldOut") }}
</div>
</div>
</v-col>
<v-col class="d-flex align-end flex-column my-auto">
<div v-if="product.discount == 0" class="text-h3">{{ product.price }} </div>
<div v-else class="d-flex align-center flex-column my-auto">
<div class="text-h3">
<span class="text-red-lighten-1"> {{ calcProductPrice(product, nrOfArticles) }} </span>
<span class="text-h6 ml-2 text-decoration-line-through">{{ product.price }} </span>
<span class="text-h6 ml-4 mb-1 bg-red">-{{ product.discount }} %</span>
</div>
</div>
</v-col>
</v-row>
<v-col class="py-0">
<v-sheet color="white">
<v-img :src="selectedImage" height="600" />
</v-sheet>
</v-col>
</v-row>
</v-container>
<v-row>
<v-spacer />
<v-col v-for="image in product.images">
<v-card
width="60"
@click="selectedImage = 'http://localhost:3000/static/' + image"
color="white"
>
<v-img :src="'http://localhost:3000/static/' + image" height="60" />
</v-card>
</v-col>
<v-spacer />
</v-row>
</v-col>
<!-- Product description col -->
<v-col>
<v-row>
<v-col class="text-h6 pt-0">
{{ $t("product.description") }}
</v-col>
</v-row>
<v-row class="text-body-1 mt-0" style="height: 450px; overflow-y: auto;">
<div v-if="product.description.length != 0">
<v-col class="text-body-1">
{{ product.description }}
</v-col>
</div>
<div v-if="product.specs.length > 1">
<v-list>
<v-list-item v-for="spec in product.specs">
<v-icon icon="mdi-circle-small" /> {{ spec }}
</v-list-item>
</v-list>
</div>
</v-row>
<v-divider class="my-4" />
<v-row>
<v-col class="pb-0">
<div class="d-flex align-center flex-column">
<div class="text-h3"> {{ product.rating }} <span class="text-h6 ml-n3">/5</span> </div>
<v-rating :model-value="product.rating" color="yellow-darken-3" half-increments disabled />
</div>
</v-col>
</v-row>
<v-divider class="my-4" />
<v-row>
<v-col cols="3">
<div class="pt-3">
<div v-if="product.inStock > 5" class="text-green-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else-if="product.inStock > 0" class="text-orange-lighten-1">
{{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div>
<div v-else class="text-red">
{{ $t("product.soldOut") }}
</div>
</div>
</v-col>
<v-col class="d-flex align-end flex-column my-auto">
<div v-if="product.discount == 0" class="text-h3">{{ product.price }} </div>
<div v-else class="d-flex align-center flex-column my-auto">
<div class="text-h3">
<span class="text-red-lighten-1"> {{ calcProductPrice(product, nrOfArticles) }} </span>
<span class="text-h6 ml-2 text-decoration-line-through">{{ product.price }} </span>
<span class="text-h6 ml-4 mb-1 bg-red">-{{ product.discount }} %</span>
</div>
</div>
</v-col>
</v-row>
</v-col>
</v-row>
<template #actions>
<v-number-input