5 Commits

36 changed files with 602 additions and 414 deletions

View File

@@ -1,4 +1,4 @@
# HackMyCart # EventMaster
The most hackable Web Shop! The most hackable Web Shop!

View File

@@ -27,6 +27,7 @@ account.get("/", verifyToken, (req: Request, res: Response) => {
account.get("/account/login", async (req: Request, res: Response) => { account.get("/account/login", async (req: Request, res: Response) => {
const encryptedPassword = encryptString(String(req.query.password)) const encryptedPassword = encryptString(String(req.query.password))
try {
// Using raw SQL code for SQL injections! // Using raw SQL code for SQL injections!
const [results, metadata] = const [results, metadata] =
await sequelize.query( await sequelize.query(
@@ -51,6 +52,9 @@ account.get("/account/login", async (req: Request, res: Response) => {
message: "Unauthorized" message: "Unauthorized"
}) })
} }
} catch (e) {
res.status(500).send()
}
}) })
@@ -123,32 +127,28 @@ account.patch("/account", verifyToken, (req: Request, res: Response) => {
where: { id: req.body.id } where: { id: req.body.id }
}) })
.then(async result => { .then(async result => {
Payment.destroy({
where: {
accountId: req.body.id
}
})
Address.destroy({
where: {
accountId: req.body.id
}
})
for (let payment of req.body.payments) { for (let payment of req.body.payments) {
if (payment.id == undefined) {
payment["accountId"] = req.body.id payment["accountId"] = req.body.id
await Payment.create(payment) await Payment.create(payment)
} else {
await Payment.update(payment,
{
where: { id: payment.id }
}
)
}
} }
for (let address of req.body.addresses) { for (let address of req.body.addresses) {
if (address.id == undefined) {
address["accountId"] = req.body.id address["accountId"] = req.body.id
await Address.create(address) await Address.create(address)
} else {
await Address.update(address,
{
where: { id: address.id }
}
)
}
} }
// Status: 200 OK // Status: 200 OK

View File

@@ -21,16 +21,14 @@ band.get("/", (req: Request, res: Response) => {
Band.findAll({ Band.findAll({
include: [ include: [
{
model: Rating,
},
{ {
model: Genre, model: Genre,
attributes: { attributes: {
exclude: [ "id" ] exclude: [ "id" ]
} }
}, },
Concert Concert,
Rating
] ]
}) })
.then(bands => { .then(bands => {

View File

@@ -3,6 +3,7 @@ import fs, { createReadStream } from "fs"
import multer from "multer" import multer from "multer"
const upload = multer({ dest: './backend/images/' }) const upload = multer({ dest: './backend/images/' })
import licenses from "../data/licenses.json" import licenses from "../data/licenses.json"
import path from 'path'
export const files = Router() export const files = Router()
@@ -10,13 +11,13 @@ export const files = Router()
* Get all folders * Get all folders
*/ */
files.get("/folders", async (req: Request, res: Response) => { files.get("/folders", async (req: Request, res: Response) => {
let dirNames = fs.readdirSync("./backend/images") let dirNames = fs.readdirSync(path.resolve(__dirname, "../images"))
let result = [] let result = []
dirNames.forEach(dir => { dirNames.forEach(dir => {
result.push({ result.push({
name: dir, name: dir,
nrOfItems: fs.readdirSync("./backend/images/" + dir).length nrOfItems: fs.readdirSync(path.resolve(__dirname, "../images/" + dir)).length
}) })
}) })
@@ -31,19 +32,20 @@ files.get("/folders", async (req: Request, res: Response) => {
*/ */
files.get("/:folder", async (req: Request, res: Response) => { files.get("/:folder", async (req: Request, res: Response) => {
let result = [] let result = []
let fileNames = fs.readdirSync("./backend/images/" + req.params.folder + "/") let fileNames = fs.readdirSync(path.resolve(__dirname, "../images/" + req.params.folder))
try {
fileNames.forEach(file => { fileNames.forEach(file => {
let resData = "" let resData = ""
let url = "http://localhost:3000/static/" + req.params.folder + "/" + file let url = "http://localhost:3000/static/" + req.params.folder + "/" + file
if (file.endsWith("html") || file.endsWith("js")) { if (file.endsWith("html") || file.endsWith("js")) {
resData = fs.readFileSync("./backend/images/" + req.params.folder + "/" + file, "utf8") resData = fs.readFileSync(path.resolve(__dirname, "../images/" + req.params.folder + "/" + file), "utf8")
} }
result.push({ result.push({
name: file, name: file,
size: fs.statSync("./backend/images/" + req.params.folder + "/" + file).size, size: fs.statSync(path.resolve(__dirname, "../images/" + req.params.folder + "/" + file)).size,
content: resData, content: resData,
url: url, url: url,
copyright: licenses.find(data => data.image == file) copyright: licenses.find(data => data.image == file)
@@ -51,6 +53,9 @@ files.get("/:folder", async (req: Request, res: Response) => {
}) })
res.status(200).json(result) res.status(200).json(result)
} catch (error) {
res.status(400).json(error)
}
}) })

View File

@@ -1,6 +1,6 @@
{ {
"name": "eventmaster", "name": "eventmaster",
"version": "0.1.0", "version": "0.2.0",
"author": "Tobias Zoghaib", "author": "Tobias Zoghaib",
"description": "Hackable ticket store for educational purposes", "description": "Hackable ticket store for educational purposes",
"license": "MIT", "license": "MIT",

View File

@@ -12,7 +12,7 @@ defineProps({
</script> </script>
<template> <template>
<v-card variant="outlined" class="my-1 mx-2 px-2"> <v-card variant="outlined" class="my-1 px-2">
<v-row class="d-flex justify-center align-center"> <v-row class="d-flex justify-center align-center">
<v-col class="text-caption text-left" v-if="descriptionText.length > 0"> <v-col class="text-caption text-left" v-if="descriptionText.length > 0">
{{ descriptionText }} {{ descriptionText }}

View File

@@ -37,7 +37,7 @@ defineProps({
{{ secondLine }} {{ secondLine }}
</v-skeleton-loader> </v-skeleton-loader>
<template #actions> <template #actions v-if="!$slots.actions">
<outlined-button <outlined-button
@click="router.push(buttonRoute)" @click="router.push(buttonRoute)"
:loading="loading" :loading="loading"
@@ -45,6 +45,10 @@ defineProps({
{{ $t('misc.actions.more') }} {{ $t('misc.actions.more') }}
</outlined-button> </outlined-button>
</template> </template>
<template #actions v-else>
<slot name="actions"></slot>
</template>
</card-view> </card-view>
</v-col> </v-col>
</template> </template>

View File

@@ -74,7 +74,7 @@
"emailIsNotValid": "Ungültige E-Mail Addresse", "emailIsNotValid": "Ungültige E-Mail Addresse",
"emailRequired": "E-Mail-Adresse benötigt", "emailRequired": "E-Mail-Adresse benötigt",
"accountManagement": "Account verwalten", "accountManagement": "Account verwalten",
"accountManagementDescription": "Persönliche Daten, Adressen, Bezahlmethoden", "accountManagementDescription": "Persönliche Daten, Konto löschen",
"login": { "login": {
"pleaseLoginToOrder": "Bitte anmelden zum bestellen", "pleaseLoginToOrder": "Bitte anmelden zum bestellen",
"backToLogin": "Zurück zum Login", "backToLogin": "Zurück zum Login",
@@ -109,7 +109,16 @@
"addNewAccount": "Neuen Account hinzufügen", "addNewAccount": "Neuen Account hinzufügen",
"accountRole": "Account Rolle", "accountRole": "Account Rolle",
"noRealPaymentsNeeded": "Keine echten Kontodaten nötig!", "noRealPaymentsNeeded": "Keine echten Kontodaten nötig!",
"administrator": "Administrator | Administratoren" "administrator": "Administrator | Administratoren",
"managePaymentsDescription": "Bezahlarten hinzufügen, ändern, löschen",
"paymentsManagement": "Bezahlarten verwalten",
"payments": {
"editPayment": "Bezahlart bearbeiten",
"editAddress": "Adresse bearbeiten"
},
"addressManagementDetails": "Adressen hinzufügen, ändern, löschen",
"addressManagement": "Adressen verwalten",
"sessionTime": "Session time"
}, },
"order": { "order": {
"oclock": "Uhr", "oclock": "Uhr",

View File

@@ -74,7 +74,7 @@
"emailIsNotValid": "E-Mail not valid", "emailIsNotValid": "E-Mail not valid",
"emailRequired": "E-Mail required", "emailRequired": "E-Mail required",
"accountManagement": "Manage Account", "accountManagement": "Manage Account",
"accountManagementDescription": "Personal data, addresses, payments", "accountManagementDescription": "Personal data, delete account",
"login": { "login": {
"pleaseLoginToOrder": "Please login to order", "pleaseLoginToOrder": "Please login to order",
"backToLogin": "Back to Login", "backToLogin": "Back to Login",
@@ -109,7 +109,16 @@
"addNewAccount": "Add new account", "addNewAccount": "Add new account",
"accountRole": "Account Role", "accountRole": "Account Role",
"noRealPaymentsNeeded": "No real payment data required!", "noRealPaymentsNeeded": "No real payment data required!",
"administrator": "Administrator" "administrator": "Administrator",
"managePaymentsDescription": "Add, change, remove payments",
"paymentsManagement": "Manage payments",
"payments": {
"editPayment": "Edit Payment",
"editAddress": "Edit address"
},
"addressManagementDetails": "Add, change, remove addresses",
"addressManagement": "Manage addresses",
"sessionTime": "Session time"
}, },
"order": { "order": {
"oclock": "o'clock", "oclock": "o'clock",

View File

@@ -0,0 +1,79 @@
<script setup lang="ts">
import actionDialog from '@/components/basics/actionDialog.vue';
import OutlinedButton from '@/components/basics/outlinedButton.vue';
import { getIbanRules, getNumberStartRules, getPostalRules, getStringRules } from '@/scripts/validationRules';
import { useAccountStore } from '@/stores/account.store';
import cardViewOneLine from '@/components/basics/cardViewOneLine.vue';
import { ref } from 'vue';
const valid = ref(false)
const accountStore = useAccountStore()
</script>
<template>
<action-dialog
v-model="accountStore.showEditDialog"
max-width="800"
:title="$t('account.payments.editAddress')"
>
<v-container>
<v-form v-model="valid">
<v-row class="pt-5">
<v-col>
<v-text-field
:label="$t('account.userData.street')"
v-model="accountStore.address.street"
:rules="getStringRules()"
variant="outlined"
clearable
/>
</v-col>
<v-col>
<v-text-field
:label="$t('account.userData.houseNumber')"
v-model="accountStore.address.houseNumber"
:rules="getNumberStartRules()"
variant="outlined"
clearable
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('account.userData.postalCode')"
v-model="accountStore.address.postalCode"
:rules="getPostalRules()"
variant="outlined"
clearable
/>
</v-col>
<v-col>
<v-text-field
:label="$t('account.userData.placeOfResidence')"
v-model="accountStore.address.city"
:rules="getStringRules()"
variant="outlined"
clearable
/>
</v-col>
</v-row>
</v-form>
</v-container>
<template #actions>
<outlined-button
color="success"
prepend-icon="mdi-content-save"
:disabled="!valid"
:loading="accountStore.fetchInProgress"
@click="accountStore.saveAddress"
>
{{ $t('misc.actions.save') }}
</outlined-button>
</template>
</action-dialog>
</template>

View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
import dataLayout from '@/layouts/dataLayout.vue';
import { useAccountStore } from '@/stores/account.store';
import { useFeedbackStore } from '@/stores/feedback.store';
import addressEditDialog from './addressEditDialog.vue';
const accountStore = useAccountStore()
const feedbackStore = useFeedbackStore()
const headers = [
{ title: feedbackStore.i18n.t('account.userData.street'), value: "street" },
{ title: feedbackStore.i18n.t('account.userData.houseNumber'), value: "houseNumber" },
{ title: feedbackStore.i18n.t('account.userData.postalCode'), value: "postalCode" },
{ title: feedbackStore.i18n.t('account.userData.placeOfResidence'), value: "city" },
{ title: "Aktionen", value: "actions", width: 130 }
]
accountStore.refreshAccount()
</script>
<template>
<data-layout
:add-button-string="$t('misc.actions.add')"
:fetch-in-progress="accountStore.fetchInProgress"
:on-add-click="() => { accountStore.newAddress() }"
>
<v-data-table
:headers="headers"
:items="accountStore.userAccount.addresses"
:loading="accountStore.fetchInProgress"
>
<template #item.actions="{ item }">
<v-btn
icon="mdi-pencil"
variant="plain"
color="orange"
@click="accountStore.editAddress(item)"
/>
<v-btn
icon="mdi-delete"
variant="plain"
color="red"
@click="accountStore.removeAddress(item)"
/>
</template>
</v-data-table>
</data-layout>
<address-edit-dialog />
</template>

View File

@@ -81,7 +81,6 @@ const stringRules = [
v-model="accountStore.userAccount.firstName" v-model="accountStore.userAccount.firstName"
variant="outlined" variant="outlined"
:rules="stringRules" :rules="stringRules"
hide-details
/> />
</v-col> </v-col>
<v-col> <v-col>
@@ -90,7 +89,6 @@ const stringRules = [
v-model="accountStore.userAccount.lastName" v-model="accountStore.userAccount.lastName"
variant="outlined" variant="outlined"
:rules="stringRules" :rules="stringRules"
hide-details
/> />
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -1,111 +0,0 @@
<script setup lang="ts">
import cardView from '@/components/basics/cardView.vue';
import { useAccountStore } from '@/stores/account.store';
import outlinedButton from '@/components/basics/outlinedButton.vue';
import { AddressModel } from '@/data/models/user/addressModel';
import { getNumberStartRules, getPostalRules, getStringRules } from '@/scripts/validationRules';
const accountStore = useAccountStore()
</script>
<template>
<card-view
icon="mdi-home"
:title="$t('account.userData.address', 2)"
>
<v-expansion-panels v-if="accountStore.userAccount.addresses.length > 0">
<v-expansion-panel
v-for="address in accountStore.userAccount.addresses"
color="primary"
>
<template #title>
<div v-if="address.street != undefined">
{{ address.street }}
</div>
&nbsp;
<div v-if="address.houseNumber != undefined">
{{ address.houseNumber }}
</div>
</template>
<template #text>
<v-row class="pt-5">
<v-col>
<v-text-field
:label="$t('account.userData.street')"
v-model="address.street"
:rules="getStringRules()"
variant="outlined"
clearable
hide-details
/>
</v-col>
<v-col>
<v-text-field
:label="$t('account.userData.houseNumber')"
v-model="address.houseNumber"
:rules="getNumberStartRules()"
variant="outlined"
clearable
hide-details
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('account.userData.postalCode')"
v-model="address.postalCode"
:rules="getPostalRules()"
variant="outlined"
clearable
hide-details
/>
</v-col>
<v-col>
<v-text-field
:label="$t('account.userData.placeOfResidence')"
v-model="address.city"
:rules="getStringRules()"
variant="outlined"
clearable
hide-details
/>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center align-center">
<outlined-button
@click="accountStore.removeAddress(address)"
color="error"
prepend-icon="mdi-delete"
>
{{ $t('misc.actions.remove') }}
</outlined-button>
</v-col>
</v-row>
</template>
</v-expansion-panel>
</v-expansion-panels>
<v-empty-state
v-else
:title="$t('account.noAddresses')"
icon="mdi-home-off"
/>
<template #actions>
<outlined-button
@click="accountStore.userAccount.addresses.push(new AddressModel())"
prepend-icon="mdi-plus"
color="success"
>
{{ $t('misc.actions.add') }}
</outlined-button>
</template>
</card-view>
</template>

View File

@@ -1,8 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import accountDataCard from './accountDataCard.vue'; import accountDataCard from './accountDataCard.vue';
import accountManagingCard from './accountManagingCard.vue'; import accountManagingCard from './accountManagingCard.vue';
import addressesCard from './addressesCard.vue';
import paymentsCard from './paymentsCard.vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import accountSubPageLayout from '@/layouts/accountSubPageLayout.vue'; import accountSubPageLayout from '@/layouts/accountSubPageLayout.vue';
@@ -17,18 +15,6 @@ const router = useRouter()
</v-col> </v-col>
</v-row> </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-row>
<v-col> <v-col>
<account-managing-card /> <account-managing-card />

View File

@@ -1,97 +0,0 @@
<script setup lang="ts">
import cardView from '@/components/basics/cardView.vue';
import { useAccountStore } from '@/stores/account.store';
import outlinedButton from '@/components/basics/outlinedButton.vue';
import { PaymentModel } from '@/data/models/user/paymentModel';
import { getIbanRules, getStringRules } from '@/scripts/validationRules';
import cardViewOneLine from '@/components/basics/cardViewOneLine.vue';
const accountStore = useAccountStore()
</script>
<template>
<card-view
icon="mdi-currency-usd"
:title="$t('account.userData.payment', 2)"
>
<v-row>
<v-col>
<card-view-one-line
color="warning"
prepend-icon="mdi-alert"
:title="$t('account.noRealPaymentsNeeded')"
/>
</v-col>
</v-row>
<v-row v-if="accountStore.userAccount.payments.length > 0">
<v-col>
<v-expansion-panels>
<v-expansion-panel
v-for="payment in accountStore.userAccount.payments"
color="primary"
>
<template #title>
{{ payment.bankName }}
</template>
<template #text>
<v-row class="pt-5">
<v-col>
<v-text-field
:label="$t('account.userData.bankName')"
v-model="payment.bankName"
:rules="getStringRules()"
variant="outlined"
hide-details
/>
</v-col>
<v-col>
<v-text-field
:label="$t('account.userData.iban')"
v-model="payment.iban"
:rules="getIbanRules()"
variant="outlined"
hide-details
/>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center align-center">
<outlined-button
@click="accountStore.removePayment(payment)"
color="error"
prepend-icon="mdi-delete"
>
{{ $t('misc.actions.remove') }}
</outlined-button>
</v-col>
</v-row>
</template>
</v-expansion-panel>
</v-expansion-panels>
</v-col>
</v-row>
<v-row v-else>
<v-col>
<v-empty-state
:title="$t('account.noPayments')"
icon="mdi-currency-usd-off"
/>
</v-col>
</v-row>
<template #actions>
<outlined-button
@click="accountStore.userAccount.payments.push(new PaymentModel())"
prepend-icon="mdi-plus"
color="success"
>
{{ $t('misc.actions.add') }}
</outlined-button>
</template>
</card-view>
</template>

View File

@@ -1,60 +1,76 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAccountStore } from '@/stores/account.store'; import { useAccountStore } from '@/stores/account.store';
import cardView from '@/components/basics/cardView.vue'; import dashboardCard from '@/components/pageParts/dashboardCard.vue';
import { useOrderStore } from '@/stores/order.store';
import OutlinedButton from '@/components/basics/outlinedButton.vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import moment from 'moment';
import { millisecondsToHumanReadableString } from '@/scripts/dateTimeScripts';
const accountStore = useAccountStore() const accountStore = useAccountStore()
const orderStore = useOrderStore()
const router = useRouter() const router = useRouter()
orderStore.getOrdersOfAccount(accountStore.userAccount)
accountStore.refreshAccount()
</script> </script>
<template> <template>
<v-container max-width="1000">
<v-row>
<v-col>
<card-view
:title="$t('misc.greeting', { msg: accountStore.userAccount.username })"
icon="mdi-hand-wave"
>
<v-container> <v-container>
<v-row> <v-row>
<v-col> <dashboard-card
<card-view
:title="$t('order.order', 2)" :title="$t('order.order', 2)"
icon="mdi-basket-check" icon="mdi-basket-check"
@click="router.push('/account/orders')" :first-line="orderStore.orders.length + ' ' + $t('order.order', 2)"
> :second-line="$t('order.ordersDescription')"
{{ $t('order.ordersDescription') }} button-route="/account/orders"
</card-view> :loading="orderStore.fetchInProgress"
</v-col> />
</v-row>
<v-row> <dashboard-card
<v-col>
<card-view
:title="$t('account.accountManagement')" :title="$t('account.accountManagement')"
icon="mdi-account" icon="mdi-account"
@click="router.push('/account/data')" :first-line="accountStore.userAccount.username"
> :second-line="$t('account.accountManagementDescription')"
{{ $t('account.accountManagementDescription') }} :loading="accountStore.fetchInProgress"
</card-view> button-route="/account/data"
</v-col> />
</v-row>
<v-row> <dashboard-card
<v-col> :title="$t('account.addressManagement')"
<card-view icon="mdi-city"
:first-line="accountStore.userAccount.addresses?.length + ' ' +
$t('account.userData.address', accountStore.userAccount.addresses?.length)"
:second-line="$t('account.addressManagementDetails')"
:loading="accountStore.fetchInProgress"
button-route="/account/addresses"
/>
<dashboard-card
:title="$t('account.paymentsManagement', 2)"
icon="mdi-currency-eur"
:first-line="accountStore.userAccount.payments?.length + ' ' +
$t('account.userData.payment', accountStore.userAccount.payments?.length)"
:second-line="$t('account.managePaymentsDescription')"
:loading="accountStore.fetchInProgress"
button-route="/account/payments"
/>
<dashboard-card
:title="$t('account.logout.logout')" :title="$t('account.logout.logout')"
:first-line="millisecondsToHumanReadableString(moment().diff(moment(accountStore.loggedInTimeStamp))) + ' h ' + $t('account.sessionTime')"
:second-line="$t('account.logout.logoutDescription')"
icon="mdi-logout" icon="mdi-logout"
>
<template #actions>
<outlined-button
color="error"
@click="accountStore.logout(); router.push('/account/login')" @click="accountStore.logout(); router.push('/account/login')"
> >
{{ $t('account.logout.logoutDescription') }} {{ $t('account.logout.logout') }}
</card-view> </outlined-button>
</v-col> </template>
</v-row> </dashboard-card>
</v-container>
</card-view>
</v-col>
</v-row> </v-row>
</v-container> </v-container>
</template> </template>

View File

@@ -0,0 +1,49 @@
<script setup lang="ts">
import dataLayout from '@/layouts/dataLayout.vue';
import { useAccountStore } from '@/stores/account.store';
import { useFeedbackStore } from '@/stores/feedback.store';
import PaymentEditDialog from './paymentEditDialog.vue';
const accountStore = useAccountStore()
const feedbackStore = useFeedbackStore()
const headers = [
{ title: feedbackStore.i18n.t('account.userData.bankName'), value: "bankName" },
{ title: feedbackStore.i18n.t('account.userData.iban'), value: "iban" },
{ title: "Aktionen", value: "actions", width: 130 }
]
accountStore.refreshAccount()
</script>
<template>
<data-layout
:add-button-string="$t('misc.actions.add')"
:fetch-in-progress="accountStore.fetchInProgress"
:on-add-click="() => { accountStore.newPayment() }"
>
<v-data-table
:headers="headers"
:items="accountStore.userAccount.payments"
:loading="accountStore.fetchInProgress"
>
<template #item.actions="{ item }">
<v-btn
icon="mdi-pencil"
variant="plain"
color="orange"
@click="accountStore.editPayment(item)"
/>
<v-btn
icon="mdi-delete"
variant="plain"
color="red"
@click="accountStore.removePayment(item)"
/>
</template>
</v-data-table>
</data-layout>
<payment-edit-dialog />
</template>

View File

@@ -0,0 +1,69 @@
<script setup lang="ts">
import actionDialog from '@/components/basics/actionDialog.vue';
import OutlinedButton from '@/components/basics/outlinedButton.vue';
import { getIbanRules, getStringRules } from '@/scripts/validationRules';
import { useAccountStore } from '@/stores/account.store';
import cardViewOneLine from '@/components/basics/cardViewOneLine.vue';
import { ref } from 'vue';
const valid = ref(false)
const accountStore = useAccountStore()
</script>
<template>
<action-dialog
v-model="accountStore.showEditDialog"
max-width="800"
:title="$t('account.payments.editPayment')"
>
<v-container>
<v-row>
<v-col>
<card-view-one-line
color="warning"
prepend-icon="mdi-alert"
:title="$t('account.noRealPaymentsNeeded')"
/>
</v-col>
</v-row>
<v-form v-model="valid">
<v-row>
<v-col>
<v-text-field
:label="$t('account.userData.bankName')"
v-model="accountStore.payment.bankName"
:rules="getStringRules(8)"
variant="outlined"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
:label="$t('account.userData.iban')"
v-model="accountStore.payment.iban"
:rules="getIbanRules()"
variant="outlined"
/>
</v-col>
</v-row>
</v-form>
</v-container>
<template #actions>
<outlined-button
color="success"
prepend-icon="mdi-content-save"
:disabled="!valid"
:loading="accountStore.fetchInProgress"
@click="accountStore.savePayment"
>
{{ $t('misc.actions.save') }}
</outlined-button>
</template>
</action-dialog>
</template>

View File

@@ -10,6 +10,7 @@ const accountStore = useAccountStore()
async function registerAccount() { async function registerAccount() {
accountStore.registerAccount() accountStore.registerAccount()
.then(result => { .then(result => {
console.log(result)
if (result) { if (result) {
showRegisterCard.value = false showRegisterCard.value = false
} }

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAccountStore } from '@/stores/account.store'; import { useAccountStore } from '@/stores/account.store';
import adminDataLayout from '@/layouts/adminDataLayout.vue'; import dataLayout from '@/layouts/dataLayout.vue';
import { useFeedbackStore } from '@/stores/feedback.store'; import { useFeedbackStore } from '@/stores/feedback.store';
const accountStore = useAccountStore() const accountStore = useAccountStore()
@@ -19,7 +19,7 @@ accountStore.getAllAccounts()
</script> </script>
<template> <template>
<admin-data-layout <data-layout
:add-button-string="$t('account.addNewAccount')" :add-button-string="$t('account.addNewAccount')"
:fetch-in-progress="accountStore.fetchInProgress" :fetch-in-progress="accountStore.fetchInProgress"
> >
@@ -44,5 +44,5 @@ accountStore.getAllAccounts()
/> --> /> -->
</template> </template>
</v-data-table> </v-data-table>
</admin-data-layout> </data-layout>
</template> </template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useBandStore } from '@/stores/band.store'; import { useBandStore } from '@/stores/band.store';
import bandEditDialog from './bandEditDialog.vue'; import bandEditDialog from './bandEditDialog.vue';
import adminDataLayout from '@/layouts/adminDataLayout.vue'; import dataLayout from '@/layouts/dataLayout.vue';
import { useFeedbackStore } from '@/stores/feedback.store'; import { useFeedbackStore } from '@/stores/feedback.store';
const bandStore = useBandStore() const bandStore = useBandStore()
@@ -22,7 +22,7 @@ bandStore.getBands()
</script> </script>
<template> <template>
<admin-data-layout <data-layout
:add-button-string="$t('band.addNewBand')" :add-button-string="$t('band.addNewBand')"
:fetch-in-progress="bandStore.fetchInProgress" :fetch-in-progress="bandStore.fetchInProgress"
:on-add-click="() => bandStore.newBand()" :on-add-click="() => bandStore.newBand()"
@@ -72,7 +72,7 @@ bandStore.getBands()
/> --> /> -->
</template> </template>
</v-data-table> </v-data-table>
</admin-data-layout> </data-layout>
<band-edit-dialog /> <band-edit-dialog />
</template> </template>

View File

@@ -2,7 +2,7 @@
import { useBandStore } from '@/stores/band.store'; import { useBandStore } from '@/stores/band.store';
import { useConcertStore } from '@/stores/concert.store'; import { useConcertStore } from '@/stores/concert.store';
import { useFeedbackStore } from '@/stores/feedback.store'; import { useFeedbackStore } from '@/stores/feedback.store';
import adminDataLayout from '@/layouts/adminDataLayout.vue'; import dataLayout from '@/layouts/dataLayout.vue';
import moment from 'moment'; import moment from 'moment';
const concertStore = useConcertStore() const concertStore = useConcertStore()
@@ -25,7 +25,7 @@ concertStore.getConcerts()
</script> </script>
<template> <template>
<admin-data-layout <data-layout
:add-button-string="$t('concert.addNewConcert')" :add-button-string="$t('concert.addNewConcert')"
:fetch-in-progress="concertStore.fetchInProgress" :fetch-in-progress="concertStore.fetchInProgress"
:on-add-click="() => concertStore.newConcert()" :on-add-click="() => concertStore.newConcert()"
@@ -73,5 +73,5 @@ concertStore.getConcerts()
/> --> /> -->
</template> </template>
</v-data-table> </v-data-table>
</admin-data-layout> </data-layout>
</template> </template>

View File

@@ -5,7 +5,7 @@ import { useAccountStore } from '@/stores/account.store';
import { useLocationStore } from '@/stores/location.store'; import { useLocationStore } from '@/stores/location.store';
import { useGenreStore } from '@/stores/genre.store'; import { useGenreStore } from '@/stores/genre.store';
import { usePreferencesStore } from '@/stores/preferences.store'; import { usePreferencesStore } from '@/stores/preferences.store';
import dashboardCard from './dashboardCard.vue'; import dashboardCard from '../../../components/pageParts/dashboardCard.vue';
import { useOrderStore } from '@/stores/order.store'; import { useOrderStore } from '@/stores/order.store';
import { useFilesStore } from '@/stores/files.store'; import { useFilesStore } from '@/stores/files.store';

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import adminDataLayout from '@/layouts/adminDataLayout.vue'; import dataLayout from '@/layouts/dataLayout.vue';
import { ref } from 'vue'; import { ref } from 'vue';
import FileUploadDialog from './fileUploadDialog.vue'; import FileUploadDialog from './fileUploadDialog.vue';
import { useFilesStore } from '@/stores/files.store'; import { useFilesStore } from '@/stores/files.store';
@@ -12,7 +12,7 @@ filesStore.getStaticFolders()
</script> </script>
<template> <template>
<admin-data-layout <data-layout
:add-button-string="$t('misc.uploadFile')" :add-button-string="$t('misc.uploadFile')"
:fetch-in-progress="filesStore.fetchInProgress" :fetch-in-progress="filesStore.fetchInProgress"
:on-add-click="() => { filesStore.showFileUploadDialog = true }" :on-add-click="() => { filesStore.showFileUploadDialog = true }"
@@ -112,7 +112,7 @@ filesStore.getStaticFolders()
</v-row> </v-row>
</v-col> </v-col>
</v-row> </v-row>
</admin-data-layout> </data-layout>
<file-preview-dialog <file-preview-dialog
v-model:show-dialog="showPreviewDialog" v-model:show-dialog="showPreviewDialog"

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import adminDataLayout from '@/layouts/adminDataLayout.vue'; import dataLayout from '@/layouts/dataLayout.vue';
import genreEditDialog from './genreEditDialog.vue'; import genreEditDialog from './genreEditDialog.vue';
import { useGenreStore } from '@/stores/genre.store'; import { useGenreStore } from '@/stores/genre.store';
@@ -15,7 +15,7 @@ genreStore.getGenres()
</script> </script>
<template> <template>
<admin-data-layout <data-layout
:add-button-string="$t('band.addNewGenre')" :add-button-string="$t('band.addNewGenre')"
:fetch-in-progress="genreStore.fetchInProgress" :fetch-in-progress="genreStore.fetchInProgress"
:on-add-click="() => { genreStore.newGenre() }" :on-add-click="() => { genreStore.newGenre() }"
@@ -48,7 +48,7 @@ genreStore.getGenres()
/> --> /> -->
</template> </template>
</v-data-table> </v-data-table>
</admin-data-layout> </data-layout>
<genre-edit-dialog /> <genre-edit-dialog />
</template> </template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import adminDataLayout from '@/layouts/adminDataLayout.vue'; import dataLayout from '@/layouts/dataLayout.vue';
import { useFeedbackStore } from '@/stores/feedback.store'; import { useFeedbackStore } from '@/stores/feedback.store';
import { useLocationStore } from '@/stores/location.store'; import { useLocationStore } from '@/stores/location.store';
@@ -22,7 +22,7 @@ locationStore.getLocations()
</script> </script>
<template> <template>
<admin-data-layout <data-layout
:fetch-in-progress="locationStore.fetchInProgress" :fetch-in-progress="locationStore.fetchInProgress"
:add-button-string="$t('location.addLocation')" :add-button-string="$t('location.addLocation')"
:on-add-click="() => { locationStore.newLocation() }" :on-add-click="() => { locationStore.newLocation() }"
@@ -66,5 +66,5 @@ locationStore.getLocations()
/> --> /> -->
</template> </template>
</v-data-table> </v-data-table>
</admin-data-layout> </data-layout>
</template> </template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import adminDataLayout from '@/layouts/adminDataLayout.vue'; import dataLayout from '@/layouts/dataLayout.vue';
import { useOrderStore } from '@/stores/order.store'; import { useOrderStore } from '@/stores/order.store';
import moment from 'moment'; import moment from 'moment';
import OrderDetailDialog from './orderDetailDialog.vue'; import OrderDetailDialog from './orderDetailDialog.vue';
@@ -20,7 +20,7 @@ orderStore.getAllOrders()
</script> </script>
<template> <template>
<admin-data-layout <data-layout
:hide-add-button="true" :hide-add-button="true"
> >
<v-data-table <v-data-table
@@ -68,7 +68,7 @@ orderStore.getAllOrders()
</template> </template>
</v-data-table> </v-data-table>
</admin-data-layout> </data-layout>
<order-detail-dialog /> <order-detail-dialog />
</template> </template>

View File

@@ -20,22 +20,19 @@ function getDotColor(exerciseGroupNr: number) {
} }
} }
function checksum(num: number) { function generateExerciseKey() {
let cs = 0
for (; num > 0; num = Math.trunc(num / 10)) {
cs += num % 10;
}
return cs
}
function generateExerciseKey(exerciseGroup: number, exerciseNr: number) {
try { try {
let matrikelNr = Number(preferencesStore.registrationNumber) let code = ""
let a = matrikelNr + exerciseGroup * 100 + exerciseNr * 12345678 +
checksum(Number(preferencesStore.registrationNumber)) * 123 for (let i = 0; i < 13; i++) {
return a.toString(16).toUpperCase() if (exerciseStore.exercises[i].solved) {
code += "3"
} else {
code += "0"
}
}
return (Number(code) + Number(preferencesStore.registrationNumber)) * 237
} catch(e) {} } catch(e) {}
} }
</script> </script>
@@ -45,20 +42,7 @@ function generateExerciseKey(exerciseGroup: number, exerciseNr: number) {
<v-row> <v-row>
<v-spacer /> <v-spacer />
<v-col
v-if="preferencesStore.studentName.length < 3 || preferencesStore.registrationNumber.length < 7"
cols="auto"
>
<card-view variant="outlined" >
{{ $t('misc.fulfillYourPersonalDataFirst') }}
</card-view>
</v-col>
<v-col cols="auto"> <v-col cols="auto">
<v-tooltip :text="$t('misc.fulfillYourPersonalDataFirst')">
<template #activator="{ props }"></template>
</v-tooltip>
<outlined-button <outlined-button
prepend-icon="mdi-file-pdf-box" prepend-icon="mdi-file-pdf-box"
@click="generateResultsPdf()" @click="generateResultsPdf()"
@@ -69,6 +53,17 @@ function generateExerciseKey(exerciseGroup: number, exerciseNr: number) {
</v-col> </v-col>
</v-row> </v-row>
<v-row>
<v-col class="text-h5 text-center">
<div>
Persönlicher Lösungsschlüssel:
</div>
<div>
{{ generateExerciseKey() }}
</div>
</v-col>
</v-row>
<v-row> <v-row>
<v-col> <v-col>
<card-view <card-view
@@ -116,9 +111,6 @@ function generateExerciseKey(exerciseGroup: number, exerciseNr: number) {
:color="exercise.solved ? 'green' : 'primary'" :color="exercise.solved ? 'green' : 'primary'"
> >
{{ preferencesStore.language == LanguageEnum.GERMAN ? exercise.descriptionDe : exercise.descriptionEn }} {{ preferencesStore.language == LanguageEnum.GERMAN ? exercise.descriptionDe : exercise.descriptionEn }}
<div class="pt-2 text-h6">
Solution Code: 0x{{ generateExerciseKey(exercise.exerciseGroup.groupNr, exercise.exerciseNr) }}
</div>
</card-view> </card-view>
</v-timeline-item> </v-timeline-item>
</template> </template>

View File

@@ -2,6 +2,7 @@
import actionDialog from '@/components/basics/actionDialog.vue'; import actionDialog from '@/components/basics/actionDialog.vue';
import outlinedButton from '@/components/basics/outlinedButton.vue'; import outlinedButton from '@/components/basics/outlinedButton.vue';
import ServerStateText from '@/components/pageParts/serverStateText.vue'; import ServerStateText from '@/components/pageParts/serverStateText.vue';
import { getRegisterNumberRules, getStringRules } from '@/scripts/validationRules';
import { useFeedbackStore } from '@/stores/feedback.store'; import { useFeedbackStore } from '@/stores/feedback.store';
import { usePreferencesStore } from '@/stores/preferences.store'; import { usePreferencesStore } from '@/stores/preferences.store';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
@@ -135,9 +136,9 @@ watch(() => currentStep.value, () => {
<v-col> <v-col>
<v-text-field <v-text-field
variant="outlined" variant="outlined"
hide-details
:label="$t('misc.yourFullName')" :label="$t('misc.yourFullName')"
v-model="preferencesStore.studentName" v-model="preferencesStore.studentName"
:rules="getStringRules(4)"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -146,9 +147,9 @@ watch(() => currentStep.value, () => {
<v-col> <v-col>
<v-text-field <v-text-field
variant="outlined" variant="outlined"
hide-details
:label="$t('misc.registrationNumber')" :label="$t('misc.registrationNumber')"
v-model="preferencesStore.registrationNumber" v-model="preferencesStore.registrationNumber"
:rules="getRegisterNumberRules()"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -177,8 +178,8 @@ watch(() => currentStep.value, () => {
<outlined-button <outlined-button
v-else v-else
@click="showDialog = false; preferencesStore.firstStartup = false" @click="showDialog = false; preferencesStore.firstStartup = false"
:disabled="preferencesStore.studentName.length == 0 || :disabled="preferencesStore.studentName.length < 5 ||
preferencesStore.registrationNumber.length == 0" preferencesStore.registrationNumber.length < 8"
prepend-icon="mdi-check" prepend-icon="mdi-check"
color="success" color="success"
> >

View File

@@ -16,6 +16,8 @@ import PreferencesPage from "@/pages/misc/preferencesPage/index.vue";
import HelpPage from "@/pages/misc/helpPage/index.vue" import HelpPage from "@/pages/misc/helpPage/index.vue"
import ErrorPage from "@/pages/misc/errorPage/index.vue" import ErrorPage from "@/pages/misc/errorPage/index.vue"
import ImageLicensePage from "@/pages/misc/imageLicensePage/index.vue" import ImageLicensePage from "@/pages/misc/imageLicensePage/index.vue"
import AccountPaymentsPage from "@/pages/account/accountPaymentsPage/index.vue"
import AccountAddressesPage from "@/pages/account/accountAddressesPage/index.vue"
const routes = [ const routes = [
// Main page // Main page
@@ -32,6 +34,8 @@ const routes = [
{ path: '/account/orders', component: OrdersPage }, { path: '/account/orders', component: OrdersPage },
{ path: '/account/data', component: AccountDataPage }, { path: '/account/data', component: AccountDataPage },
{ path: '/account/login', component: LoginPage }, { path: '/account/login', component: LoginPage },
{ path: '/account/payments', component: AccountPaymentsPage },
{ path: '/account/addresses', component: AccountAddressesPage },
// Admin // Admin
...adminRoutes, ...adminRoutes,

View File

@@ -21,3 +21,15 @@ export function dateToHumanReadableString(date: Date) {
export function dateStringToHumanReadableString(string: string) { export function dateStringToHumanReadableString(string: string) {
return dateToHumanReadableString(new Date(string)) return dateToHumanReadableString(new Date(string))
} }
/**
* Format milliseconds to a readable format
*
* @param milliseconds Milliseconds to format
*
* @returns h:mm format
*/
export function millisecondsToHumanReadableString(milliseconds: number): string {
return Math.floor(milliseconds / 1000 / 60 / 60) + ':' +
String(Math.floor(milliseconds / 60000)).padStart(2, "0") + ''
}

View File

@@ -169,3 +169,31 @@ export function getIbanRules() {
} }
] ]
} }
export function getRegisterNumberRules() {
const feedbackStore = useFeedbackStore()
return [
value => {
if (value) {
return true
} else {
return feedbackStore.i18n.t('misc.validation.required')
}
},
value => {
if (value?.length >= 8) {
return true
} else {
return feedbackStore.i18n.t('misc.validation.notEnoughChars')
}
},
value => {
if(!isNaN(value) && !isNaN(parseFloat(value))) {
return true
} else {
return feedbackStore.i18n.t('misc.validation.onlyDigitsAllowed')
}
}
]
}

View File

@@ -10,6 +10,7 @@ import { AccountApiModel } from "../data/models/user/accountApiModel";
import { ref } from "vue"; import { ref } from "vue";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { useExerciseStore } from "./exercise.store"; import { useExerciseStore } from "./exercise.store";
import moment, { Moment } from "moment";
export const useAccountStore = defineStore("accountStore", { export const useAccountStore = defineStore("accountStore", {
state: () => ({ state: () => ({
@@ -17,10 +18,10 @@ export const useAccountStore = defineStore("accountStore", {
accounts: ref<Array<AccountApiModel>>([]), accounts: ref<Array<AccountApiModel>>([]),
/** Server token of currently logged in account */ /** Server token of currently logged in account */
userAccountToken: useLocalStorage("hackmycart/accountStore/userAccountToken", ""), userAccountToken: useLocalStorage("eventMaster/accountStore/userAccountToken", ""),
/** Useraccount which is currently logged in */ /** Useraccount which is currently logged in */
userAccount: useLocalStorage("hackmycart/accountStore/userAccount", new AccountApiModel()), userAccount: useLocalStorage("eventMaster/accountStore/userAccount", new AccountApiModel()),
/** User input on login screen */ /** User input on login screen */
loginData: ref<{ username: String, password: String}>( loginData: ref<{ username: String, password: String}>(
@@ -37,7 +38,15 @@ export const useAccountStore = defineStore("accountStore", {
adminPanelVisible: ref(false), adminPanelVisible: ref(false),
/** Flag to activate buy option on basket page */ /** Flag to activate buy option on basket page */
privilegeBuy: ref(false) privilegeBuy: ref(false),
payment: ref(),
address: ref(),
showEditDialog: ref(false),
loggedInTimeStamp: useLocalStorage<string>("eventMaster/accountStore/loggedInTimeStamp", "")
}), }),
actions: { actions: {
@@ -80,6 +89,7 @@ export const useAccountStore = defineStore("accountStore", {
await getLogin(this.loginData.username, this.loginData.password) await getLogin(this.loginData.username, this.loginData.password)
.then(async result => { .then(async result => {
this.userAccountToken = result.data.token this.userAccountToken = result.data.token
this.loggedInTimeStamp = moment().format("YYYY-MM-DDTHH:mm:ss.SSS")
getAccount(this.userAccountToken) getAccount(this.userAccountToken)
.then(response => { .then(response => {
@@ -113,14 +123,16 @@ export const useAccountStore = defineStore("accountStore", {
* Reload account information about current logged in user * Reload account information about current logged in user
*/ */
async refreshAccount() { async refreshAccount() {
this.fetchInProgress = true
getAccount(this.userAccountToken) getAccount(this.userAccountToken)
.then(response => { .then(response => {
this.userAccount = response.data this.userAccount = response.data
this.fetchInProgress = false
this.privilegeBuy = true this.privilegeBuy = true
this.adminPanelVisible = response.data.accountRole.privilegeAdminPanel this.adminPanelVisible = response.data.accountRole.privilegeAdminPanel
this.fetchInProgress = false
}) })
}, },
@@ -133,6 +145,7 @@ export const useAccountStore = defineStore("accountStore", {
async registerAccount(): Promise<boolean> { async registerAccount(): Promise<boolean> {
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
const exerciseStore = useExerciseStore() const exerciseStore = useExerciseStore()
let success = false
this.fetchInProgress = true this.fetchInProgress = true
if (this.registerData.username == null || this.registerData.username.length < 4) { if (this.registerData.username == null || this.registerData.username.length < 4) {
@@ -142,7 +155,8 @@ export const useAccountStore = defineStore("accountStore", {
} else if (!this.registerData.email.match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)) { } else if (!this.registerData.email.match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)) {
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTMAILADDRESSUNVALID) feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTMAILADDRESSUNVALID)
} }
else { else
{
await registerAccount(this.registerData) await registerAccount(this.registerData)
.then(async res => { .then(async res => {
if (res.status == 201) { if (res.status == 201) {
@@ -156,6 +170,7 @@ export const useAccountStore = defineStore("accountStore", {
} }
this.fetchInProgress = false this.fetchInProgress = false
success = true
}) })
.catch((error) => { .catch((error) => {
if (error.status == 400) { if (error.status == 400) {
@@ -165,12 +180,11 @@ export const useAccountStore = defineStore("accountStore", {
} }
this.fetchInProgress = false this.fetchInProgress = false
return false
}) })
} }
this.fetchInProgress = false this.fetchInProgress = false
return false return success
}, },
/** /**
@@ -179,6 +193,7 @@ export const useAccountStore = defineStore("accountStore", {
async updateAccount() { async updateAccount() {
const feedbackStore = useFeedbackStore() const feedbackStore = useFeedbackStore()
const exerciseStore = useExerciseStore() const exerciseStore = useExerciseStore()
this.fetchInProgress = true
// Check for exercise 0.2 completion // Check for exercise 0.2 completion
let accountComplete = this.userAccount.firstName != "" && this.userAccount.lastName != "" && let accountComplete = this.userAccount.firstName != "" && this.userAccount.lastName != "" &&
@@ -196,6 +211,7 @@ export const useAccountStore = defineStore("accountStore", {
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTUPDATESUCCESSFUL) feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTUPDATESUCCESSFUL)
this.userAccount = res.data this.userAccount = res.data
this.fetchInProgress = false
} }
}) })
}, },
@@ -229,6 +245,33 @@ export const useAccountStore = defineStore("accountStore", {
}) })
}, },
newAddress() {
this.address = new AddressModel()
this.showEditDialog = true
},
editAddress(address: AddressModel) {
this.address = address
this.showEditDialog = true
},
async saveAddress() {
this.fetchInProgress = true
if (this.address.id == undefined) {
this.userAccount.addresses.push(this.address)
} else {
this.userAccount.addresses = this.userAccount.addresses.filter(address => {
return address.id != this.address.id
})
this.userAccount.addresses.push(this.address)
}
await this.updateAccount()
this.showEditDialog = false
},
/** /**
* Remove an address from the user model * Remove an address from the user model
* *
@@ -238,17 +281,59 @@ export const useAccountStore = defineStore("accountStore", {
this.userAccount.addresses = this.userAccount.addresses.filter((addr: AddressModel) => this.userAccount.addresses = this.userAccount.addresses.filter((addr: AddressModel) =>
addr != address addr != address
) )
this.updateAccount()
}, },
/** /**
* Remove an payment from the user model * Add a new payment, opens dialog
*/
newPayment() {
this.payment = new PaymentModel()
this.showEditDialog = true
},
/**
* Edit existing payment, opens dialog
*
* @param payment Payment dataset to edit
*/
editPayment(payment: PaymentModel) {
this.payment = payment
this.showEditDialog = true
},
/**
* Save current edited payment
*/
async savePayment() {
this.fetchInProgress = true
if (this.payment.id == undefined) {
this.userAccount.payments.push(this.payment)
} else {
this.userAccount.payments = this.userAccount.payments.filter(payment => {
return payment.id != this.payment.id
})
this.userAccount.payments.push(this.payment)
}
await this.updateAccount()
this.showEditDialog = false
},
/**
* Remove a payment from the user model
* *
* @param address Payment dataset to remove * @param address Payment dataset to remove
*/ */
removePayment(payment: PaymentModel) { async removePayment(payment: PaymentModel) {
this.userAccount.payments = this.userAccount.payments.filter((paym: PaymentModel) => this.userAccount.payments = await this.userAccount.payments.filter((paym: PaymentModel) =>
paym != payment paym != payment
) )
this.updateAccount()
}, },
/** /**

View File

@@ -16,7 +16,7 @@ import { useExerciseStore } from "./exercise.store";
export const useBasketStore = defineStore('basketStore', { export const useBasketStore = defineStore('basketStore', {
state: () => ({ state: () => ({
/** Items in customers basket */ /** Items in customers basket */
itemsInBasket: useLocalStorage<Array<BasketItemModel>>("hackmycart/basketStore/itemsInBasket", []), itemsInBasket: useLocalStorage<Array<BasketItemModel>>("eventMaster/basketStore/itemsInBasket", []),
/** Address used in the order dialog */ /** Address used in the order dialog */
usedAddress: ref<AddressModel>(null), usedAddress: ref<AddressModel>(null),

View File

@@ -15,10 +15,10 @@ import { AccountApiModel } from "@/data/models/user/accountApiModel";
export const usePreferencesStore = defineStore('preferencesStore', { export const usePreferencesStore = defineStore('preferencesStore', {
state: () => ({ state: () => ({
/** Selected theme by user */ /** Selected theme by user */
theme: useLocalStorage<ThemeEnum>("hackmycart/preferencesStore/theme", ThemeEnum.DARK), theme: useLocalStorage<ThemeEnum>("eventMaster/preferencesStore/theme", ThemeEnum.DARK),
/** Selected language by user */ /** Selected language by user */
language: useLocalStorage<LanguageEnum>("hackmycart/preferencesStore/language", LanguageEnum.GERMAN), language: useLocalStorage<LanguageEnum>("eventMaster/preferencesStore/language", LanguageEnum.GERMAN),
/** Request to server sent, waiting for data response */ /** Request to server sent, waiting for data response */
fetchInProgress: ref(false), fetchInProgress: ref(false),
@@ -36,13 +36,13 @@ export const usePreferencesStore = defineStore('preferencesStore', {
showFactoryResetDialog: ref(false), showFactoryResetDialog: ref(false),
/** Marks the first run of the app */ /** Marks the first run of the app */
firstStartup: useLocalStorage<Boolean>("hackmycart/preferencesStore/firstStartup", true), firstStartup: useLocalStorage<Boolean>("eventMaster/preferencesStore/firstStartup", true),
/** Full name of student */ /** Full name of student */
studentName: useLocalStorage<string>("hackmycart/preferencesStore/studentName", ""), studentName: useLocalStorage<string>("eventMaster/preferencesStore/studentName", ""),
/** Matrikel number */ /** Matrikel number */
registrationNumber: useLocalStorage<string>("hackmycart/preferencesStore/registrationNumber", "") registrationNumber: useLocalStorage<string>("eventMaster/preferencesStore/registrationNumber", "")
}), }),
actions: { actions: {