Extend database with more tables, rewrite API doc, improve API endpoints

This commit is contained in:
2024-09-23 21:22:45 +02:00
parent 6aae064902
commit 87f3516b54
41 changed files with 1345 additions and 1126 deletions

View File

@@ -0,0 +1,16 @@
{
"data": [
{
"id": 0,
"name": "User",
"privilegeBuy": true,
"privilegeAdminPanel": false
},
{
"id": 1,
"name": "Admin",
"privilegeBuy": true,
"privilegeAdminPanel": true
}
]
}

View File

@@ -4,92 +4,183 @@
"id": 0,
"username": "hagemeister93",
"password": "Xjt3qb5t",
"street": "Laportestraße",
"houseNumber": 22,
"postalCode": 30449,
"city": "Hannover",
"firstName": "Laurin",
"lastName": "Hagemeister",
"bankName": "Deutsche Bank",
"iban": "DE92500105175721645777"
"addresses": [
{
"accountId": 0,
"street": "Laportestraße",
"houseNumber": 22,
"postalCode": 30449,
"city": "Hannover"
}
],
"payments": [
{
"accountId": 0,
"bankName": "Deutsche Bank",
"iban": "DE92500105175721645777"
}
],
"accountRoleId": 1
},
{
"id": 1,
"username": "katjaStoiber",
"password": "target123",
"street": "Gustav-Adolf-Straße",
"houseNumber": 30,
"postalCode": 30167,
"city": "Hannover",
"firstName": "Katja",
"lastName": "Stoiber",
"bankName": "DZ Bank",
"iban": "DE12500105179557939114"
"addresses": [
{
"accountId": 1,
"street": "Gustav-Adolf-Straße",
"houseNumber": 30,
"postalCode": 30167,
"city": "Hannover"
}
],
"payments": [
{
"accountId": 1,
"bankName": "DZ Bank",
"iban": "DE12500105179557939114"
}
],
"accountRoleId": 0
},
{
"id": 2,
"username": "oetkerohnek",
"password": "iloveyou",
"street": "Eckermannstraße",
"houseNumber": 1,
"postalCode": 30625,
"city": "Hannover",
"firstName": "Luna",
"lastName": "Oeter",
"bankName": "Commerzbank",
"iban": "DE31500105175417833272"
"addresses": [
{
"accountId": 2,
"street": "Eckermannstraße",
"houseNumber": 1,
"postalCode": 30625,
"city": "Hannover"
},
{
"accountId": 2,
"street": "Gehrdener Straße",
"houseNumber": 14,
"postalCode": 30459,
"city": "Hannover"
}
],
"payments": [
{
"accountId": 2,
"bankName": "Commerzbank",
"iban": "DE31500105175417833272"
}
],
"accountRoleId": 0
},
{
"id": 3,
"username": "duranduran",
"password": "H4nn0ver",
"street": "Schlägerstraße",
"houseNumber": 36,
"postalCode": 30171,
"city": "Hannover",
"firstName": "Jürgen",
"lastName": "Durand",
"bankName": "ING",
"iban": "DE41500105172184936679"
"addresses": [
{
"accountId": 3,
"street": "Schlägerstraße",
"houseNumber": 36,
"postalCode": 30171,
"city": "Hannover"
}
],
"payments": [
{
"accountId": 3,
"bankName": "ING",
"iban": "DE41500105172184936679"
}
],
"accountRoleId": 0
},
{
"id": 4,
"username": "guitarhero",
"password": "gwerty123",
"street": "Steinmetzstraße",
"houseNumber": 12,
"postalCode": 30163,
"city": "Hannover",
"firstName": "Frederik",
"lastName": "Furtwängler",
"bankName": "Sparkasse Hannover",
"iban": "DE85500105172283979774"
"addresses": [
{
"accountId": 4,
"street": "Steinmetzstraße",
"houseNumber": 12,
"postalCode": 30163,
"city": "Hannover"
}
],
"payments": [
{
"accountId": 4,
"bankName": "Sparkasse Hannover",
"iban": "DE85500105172283979774"
}
],
"accountRoleId": 0
},
{
"id": 5,
"username": "herbstMareike",
"password": "qhsrbpgrs",
"street": "Allerweg",
"houseNumber": 33,
"postalCode": 30851,
"city": "Langenhagen",
"firstName": "Mareike",
"lastName": "Herbst",
"bankName": "Postbank",
"iban": "DE45500105178862417577"
"addresses": [
{
"accountId": 5,
"street": "Allerweg",
"houseNumber": 33,
"postalCode": 30851,
"city": "Langenhagen"
}
],
"payments": [
{
"accountId": 5,
"bankName": "Postbank",
"iban": "DE45500105178862417577"
}
],
"accountRoleId": 0
},
{
"id": 6,
"username": "seibertmitb",
"password": "{jkz+WvQe",
"street": "Marktstraße",
"houseNumber": 26,
"postalCode": 30880,
"city": "Laatzen",
"firstName": "Janna",
"lastName": "Seibert",
"bankName": "Sparkasse Hannover",
"iban": "DE51500105177526222196"
"addresses": [
{
"accountId": 6,
"street": "Marktstraße",
"houseNumber": 26,
"postalCode": 30880,
"city": "Laatzen"
},
{
"accountId": 6,
"street": "Kleiner Hillen",
"houseNumber": 24,
"postalCode": 30559,
"city": "Hannover"
}
],
"payments": [
{
"accountId": 6,
"bankName": "Sparkasse Hannover",
"iban": "DE51500105177526222196"
}
],
"accountRoleId": 0
}
]
}

View File

@@ -0,0 +1,40 @@
{
"data": [
{
"id": 0,
"name": "Lenovo"
},
{
"id": 1,
"name": "Puma"
},
{
"id": 2,
"name": "George Orwell"
},
{
"id": 3,
"name": "Aldous Huxley"
},
{
"id": 4,
"name": "Dell"
},
{
"id": 5,
"name": "Fender"
},
{
"id": 6,
"name": "ESP"
},
{
"id": 7,
"name": "Pearl"
},
{
"id": 8,
"name": "Apple"
}
]
}

View File

@@ -4,25 +4,29 @@
"id": 0,
"orderId": 0,
"productId": 0,
"quantity": 2
"quantity": 2,
"orderPrice": 1769.99
},
{
"id": 1,
"orderId": 1,
"productId": 6,
"quantity": 1
"quantity": 1,
"orderPrice": 899.99
},
{
"id": 2,
"orderId": 2,
"productId": 3,
"quantity": 3
"quantity": 3,
"orderPrice": 9.99
},
{
"id": 3,
"orderId": 2,
"productId": 2,
"quantity": 1
"quantity": 1,
"orderPrice": 14.99
}
]
}

View File

@@ -3,22 +3,17 @@
{
"id": 0,
"accountId": 0,
"totalPrice": 0,
"shippingProgress": 4
},
{
"id": 1,
"accountId": 3,
"totalPrice": 7.99,
"shippingProgress": 5,
"createdAt": "2024-09-09 12:24:24.225 +00:00"
"shippingProgress": 5
},
{
"id": 2,
"accountId": 3,
"totalPrice": 44.96,
"shippingProgress": 2,
"createdAt": "2024-09-12 09:57:24.225 +00:00"
"shippingProgress": 2
}
]
}

View File

@@ -2,7 +2,7 @@
"data": [
{
"id": 0,
"brand": "Lenovo",
"brandId": 0,
"name": "Thinkpad T14s Gen 4",
"price": 1769.99,
"categoryId": 1,
@@ -27,11 +27,11 @@
"thinkpad-t14s-6.avif",
"thinkpad-t14s-7.avif"
],
"storedItems": 5
"inStock": 5
},
{
"id": 1,
"brand": "Puma",
"brandId": 1,
"name": "Men's T-Shirt Black",
"price": 14.99,
"categoryId": 3,
@@ -52,11 +52,11 @@
"puma-t-shirt-men-4.jpg",
"puma-t-shirt-men-5.jpg"
],
"storedItems": 30
"inStock": 30
},
{
"id": 2,
"brand": "Puma",
"brandId": 1,
"name": "Woman's Shirt",
"price": 14.99,
"categoryId": 3,
@@ -78,11 +78,11 @@
"puma-t-shirt-woman-5.jpg",
"puma-t-shirt-woman-6.jpg"
],
"storedItems": 30
"inStock": 30
},
{
"id": 3,
"brand": "George Orwell",
"brandId": 2,
"name": "1984",
"price": 9.99,
"categoryId": 4,
@@ -108,11 +108,11 @@
"1984-2.webp",
"1984-3.webp"
],
"storedItems": 30
"inStock": 30
},
{
"id": 4,
"brand": "Aldous Huxley",
"brandId": 3,
"name": "Brave New World",
"price": 10.49,
"categoryId": 4,
@@ -135,11 +135,11 @@
"brave-new-world-1.jpg",
"brave-new-world-2.jpg"
],
"storedItems": 30
"inStock": 30
},
{
"id": 5,
"brand": "Dell",
"brandId": 4,
"name": "XPS 8960 Desktop",
"price": 1640.26,
"categoryId": 1,
@@ -160,11 +160,11 @@
"dell-xps-desktop-5.jpg",
"dell-xps-desktop-6.jpg"
],
"storedItems": 10
"inStock": 10
},
{
"id": 6,
"brand": "Fender",
"brandId": 5,
"name": "Player II Jazz Bass RW 3TS",
"price": 899.99,
"categoryId": 5,
@@ -198,11 +198,11 @@
"fender-player-ii-jazz-bass-rw-3ts-4.jpg",
"fender-player-ii-jazz-bass-rw-3ts-5.jpg"
],
"storedItems": 15
"inStock": 15
},
{
"id": 7,
"brand": "ESP",
"brandId": 6,
"name": "LTD Iron Cross SW",
"price": 2999.00,
"categoryId": 5,
@@ -239,11 +239,11 @@
"esp-lts-iron-cross-sw-5.jpg",
"esp-lts-iron-cross-sw-6.jpg"
],
"storedItems": 0
"inStock": 0
},
{
"id": 8,
"brand": "Pearl",
"brandId": 7,
"name": "Decade Maple Standard D. Black",
"price": 1444.00,
"categoryId": 5,
@@ -273,11 +273,11 @@
"pearl-decade-maple-standard-black-5.jpg",
"pearl-decade-maple-standard-black-6.jpg"
],
"storedItems": 4
"inStock": 4
},
{
"id": 9,
"brand": "Apple",
"brandId": 8,
"name": "MacBook Air 13.6 Zoll M3",
"price": 1759.00,
"categoryId": 1,
@@ -299,7 +299,7 @@
"macbook-air-4.avif",
"macbook-air-5.avif"
],
"storedItems": 18
"inStock": 18
}
]
}

View File

@@ -6,6 +6,11 @@ import { Order } from "./models/order.model"
import { OrderItem } from "./models/orderItem.model"
import { Product } from "./models/product.model"
import { Account } from "./models/account.model"
import { prepopulateDatabase } from "./scripts/databaseHelper"
import { Address } from "./models/address.model"
import { Payment } from "./models/payment.model"
import { AccountRole } from "./models/accountRole.model"
import { Brand } from "./models/brand.model"
const dbName = "database"
const dbUser = "root"
@@ -18,13 +23,16 @@ export const sequelize = new Sequelize({
username: dbUser,
password: dbPassword,
storage: "database.sqlite",
models: [ Category, Product, Account, Order, OrderItem ]
models: [ Address, Payment, AccountRole, Account, Category, Brand, Product, Order, OrderItem ]
})
export function startDatabase() {
// Create database and tables
sequelize.sync({ force: false })
.then(() => {
console.log(`Database & tables created!`)
console.log("Database & tables created!")
//prepopulateDatabase()
console.log("Database prepopulated!")
})
}

View File

@@ -1,7 +1,10 @@
import { Table, Column, Model, HasMany, Unique } from 'sequelize-typescript';
import { Table, Column, Model, HasMany, Unique, BelongsTo, ForeignKey } from 'sequelize-typescript';
import { Order } from './order.model';
import { Address } from './address.model';
import { Payment } from './payment.model';
import { AccountRole } from './accountRole.model';
@Table
@Table({ timestamps: false })
export class Account extends Model {
@Unique
@Column
@@ -16,25 +19,21 @@ export class Account extends Model {
@Column
lastName: string = ""
@ForeignKey(() => AccountRole)
@Column
street: string = ""
accountRoleId: number
@Column
houseNumber: number = 0
@Column
postalCode: number = 0
@Column
city: string
@Column
bankName: string
@Column
iban: string
// Relations
@HasMany(() => Address)
addresses: Address[]
@HasMany(() => Payment)
payments: Payment[]
@HasMany(() => Order)
orders: Order[]
@BelongsTo(() => AccountRole)
accountRole: AccountRole
}

View File

@@ -0,0 +1,19 @@
import { Column, HasMany, Model, Table } from "sequelize-typescript";
import { Account } from "./account.model";
@Table({ timestamps: false })
export class AccountRole extends Model {
@Column
name: string
@Column
privilegeBuy: boolean
@Column
privilegeAdminPanel: boolean
// Relations
@HasMany(() => Account)
accounts: Account[]
}

View File

@@ -0,0 +1,27 @@
import { BelongsTo, Column, ForeignKey, Model, Table } from "sequelize-typescript";
import { Account } from "./account.model";
@Table({ timestamps: false })
export class Address extends Model {
@ForeignKey(() => Account)
@Column
accountId: number
@Column
street: string
@Column
houseNumber: number
@Column
postalCode: number
@Column
city: string
// Relations
@BelongsTo(() => Account)
account: Account
}

View File

@@ -0,0 +1,14 @@
import { Column, HasMany, Model, Table } from "sequelize-typescript";
import { Product } from "./product.model";
@Table({ timestamps: false })
export class Brand extends Model {
@Column
name: string
// Relations
@HasMany(() => Product)
products: Product[]
}

View File

@@ -1,7 +1,7 @@
import { Table, Column, Model, BelongsTo, ForeignKey, HasMany, Unique } from 'sequelize-typescript';
import { Product } from './product.model';
@Table
@Table({ timestamps: false })
export class Category extends Model {
@Unique
@Column

View File

@@ -1,18 +1,22 @@
import { Table, Column, Model, BelongsTo, ForeignKey, HasMany, BelongsToMany } from 'sequelize-typescript';
import { Table, Column, Model, BelongsTo, ForeignKey, HasMany, BelongsToMany, Default } from 'sequelize-typescript';
import { Account } from './account.model';
import { OrderItem } from './orderItem.model';
@Table
@Table({
updatedAt: false,
createdAt: 'orderedAt'
})
export class Order extends Model {
@Column
@ForeignKey(() => Account)
accountId: number
@Column
totalPrice: number
orderedAt: Date
@Default(1)
@Column
shippingProgress: number = 1
shippingProgress: number
// Relations
@@ -20,5 +24,5 @@ export class Order extends Model {
account: Account
@HasMany(() => OrderItem)
orderItem: OrderItem[]
orderItems: OrderItem[]
}

View File

@@ -2,7 +2,7 @@ import { Model, BelongsTo, Column, ForeignKey, HasMany, HasOne, Table } from "se
import { Product } from "./product.model";
import { Order } from "./order.model";
@Table
@Table({ timestamps: false })
export class OrderItem extends Model {
@Column
@ForeignKey(() => Order)
@@ -10,6 +10,9 @@ export class OrderItem extends Model {
@Column
quantity: number
@Column
orderPrice: number
@Column
@ForeignKey(() => Product)

View File

@@ -0,0 +1,20 @@
import { BelongsTo, Column, ForeignKey, Model, Table } from "sequelize-typescript";
import { Account } from "./account.model";
@Table({ timestamps: false })
export class Payment extends Model {
@ForeignKey(() => Account)
@Column
accountId: number
@Column
bankName: string
@Column
iban: string
// Relations
@BelongsTo(() => Account)
account: Account
}

View File

@@ -1,18 +1,23 @@
import { Table, Column, Model, ForeignKey, BelongsTo, BelongsToMany, HasMany, DataType } from 'sequelize-typescript';
import { Category } from './category.model';
import { OrderItem } from './orderItem.model';
import { Brand } from './brand.model';
@Table
@Table({ timestamps: false })
export class Product extends Model {
@Column
brand: string
@ForeignKey(() => Category)
categoryId: number
@ForeignKey(() => Brand)
@Column
brandId: number
@Column
name: string
@Column
@ForeignKey(() => Category)
categoryId: number
description: string
@Column
price: number
@@ -23,19 +28,8 @@ export class Product extends Model {
@Column
rating: number
@Column({
type: DataType.STRING,
get(): Array<string> {
return this.getDataValue('images').split(';')
},
set(value: Array<string>) {
this.setDataValue('images', value.join(';'))
}
})
images: Array<string>
@Column
description: string
inStock: number
@Column({
type: DataType.STRING,
@@ -48,13 +42,26 @@ export class Product extends Model {
})
specs: Array<string>
@Column
storedItems: number
@Column({
type: DataType.STRING,
get(): Array<string> {
return this.getDataValue('images').split(';')
},
set(value: Array<string>) {
this.setDataValue('images', value.join(';'))
}
})
images: Array<string>
// Relations
@BelongsTo(() => Category)
category: Category
@BelongsTo(() => Brand)
brand: Brand
@HasMany(() => OrderItem)
order: OrderItem
orders: OrderItem[]
}

View File

@@ -1,27 +1,41 @@
import { Router, Request, Response, NextFunction } from "express";
import { Router, Request, Response } from "express";
import { Account } from "../models/account.model";
import { validateString } from "../scripts/validateHelper";
import { Address } from "../models/address.model";
import { Payment } from "../models/payment.model";
import { AccountRole } from "../models/accountRole.model";
export const account = Router()
// Login user
account.post("/login", (req: Request, res: Response) => {
Account.findOne({
raw: true,
where: { username: req.body.username }
where: { username: req.body.username },
include: [ Address, Payment, AccountRole ],
attributes: {
exclude: [
"accountRoleId"
]
}
})
.then(account => {
if (account != null) {
if (account.password == req.body.password) {
if (account.dataValues.password == req.body.password) {
// Status: 200 OK
res.status(200).json(account).send()
} else {
// Status: 401 Unauthorized
res.status(401).send()
res.status(401).json({
code: 401,
message: "Unauthorized"
}).send()
}
} else {
// Status: 400 Bad request
res.status(400).send()
res.status(400).json({
code: 400,
message: "Bad Request"
}).send()
}
}
)
@@ -34,6 +48,7 @@ account.post("/", (req: Request, res: Response) => {
{
// Status: 400 Bad request
res.status(400).json({
code: 400,
message: "Username too short!"
}).send()
}
@@ -43,6 +58,7 @@ account.post("/", (req: Request, res: Response) => {
{
// Status: 400 Bad request
res.status(400).json({
code: 400,
message: "Password too short!"
}).send()
}
@@ -54,7 +70,10 @@ account.post("/", (req: Request, res: Response) => {
res.status(201).json(account).send()
}).catch(reason => {
// Status: 409 Conflict
res.status(409).send()
res.status(409).json({
code: 409,
message: "Username already in use"
}).send()
})
})
@@ -70,7 +89,8 @@ account.patch("/", (req: Request, res: Response) => {
.catch(error => {
// Status: 400 Bad request
res.status(400).json({
message: error
code: 400,
message: error
}).send()
})
})

View File

@@ -9,19 +9,17 @@ category.get("/", (req: Request, res: Response, next: NextFunction) => {
.then(categories => {
res.status(200).json(categories).send()
})
.catch(error => {
res.status(400)
})
})
// Add new category
category.post("/", (req: Request, res: Response, next: NextFunction) => {
Category.create(req.body)
.then(category => {
res.status(201).send()
res.status(201).json(category).send()
})
.catch(error => {
res.status(400).json({
code: 400,
message: error
}).send()
})
@@ -33,10 +31,11 @@ category.delete("/:id", (req: Request, res: Response, next: NextFunction) => {
where: { id: req.params.id }
})
.then(category => {
res.status(200).send()
res.status(200).json(category).send()
})
.catch(error => {
res.status(400).json({
code: 400,
message: error
}).send()
})

View File

@@ -23,31 +23,25 @@ order.post("/", (req: Request, res: Response) => {
let totalPrice = 0
Order.create(req.body)
.then(order => {
for (let orderItem of req.body.orderItem) {
.then(async order => {
for (let orderItem of req.body.orderItems) {
OrderItem.create({
"orderId": order.id,
"quantity": orderItem.quantity,
"orderPrice": orderItem.orderPrice,
"productId": orderItem.productId
})
Product.findOne({
raw: true,
where: { id: orderItem.productId }
totalPrice += orderItem.quantity * orderItem.orderPrice
Order.update({
totalPrice: totalPrice
}, {
where: { id: order.id }
})
.then(product => {
totalPrice += product.price * orderItem.quantity
Order.update({
totalPrice: totalPrice
}, {
where: { id: order.id },
})
})
}
})
// Created
res.status(201).send()
// Created
res.status(201).json(order).send()
})
})

View File

@@ -1,13 +1,20 @@
import { Router, Request, Response, NextFunction } from "express";
import { Product } from "../models/product.model";
import { Category } from "../models/category.model";
import { Brand } from "../models/brand.model";
export const product = Router()
// Get all products
product.get("/", (req: Request, res: Response) => {
Product.findAll({
include: [ Category ]
include: [ Category, Brand ],
attributes: {
exclude: [
"categoryId",
"brandId"
]
}
})
.then(products => {
res.status(200).json(products)
@@ -16,7 +23,18 @@ product.get("/", (req: Request, res: Response) => {
// Get a product by id
product.get("/:productId", (req: Request, res: Response) => {
Product.findByPk(req.params.productId)
Product.findByPk(
req.params.productId,
{
include: [ Category, Brand ],
attributes: {
exclude: [
"categoryId",
"brandId"
]
}
}
)
.then(product => {
res.status(200).json(product).send()
})
@@ -30,6 +48,7 @@ product.post("/", (req: Request, res: Response) => {
})
.catch(error => {
res.status(400).json({
code: 400,
message: error
}).send()
})
@@ -45,6 +64,7 @@ product.delete("/:id", (req: Request, res: Response) => {
})
.catch(error => {
res.status(400).json({
code: 400,
message: error
}).send()
})

View File

@@ -3,12 +3,18 @@ import { Order } from '../models/order.model'
import { OrderItem } from '../models/orderItem.model'
import { Product } from '../models/product.model'
import { Account } from '../models/account.model'
import { Address } from '../models/address.model'
import { Payment } from '../models/payment.model'
import { AccountRole } from '../models/accountRole.model'
import { Brand } from '../models/brand.model'
import categories from "./../data/categories.json"
import products from "./../data/products.json"
import accounts from "./../data/accounts.json"
import orders from "./../data/orders.json"
import orderItems from "./../data/orderItems.json"
import accountRoles from "./../data/accountRoles.json"
import brands from "./../data/brands.json"
/**
* Delete all datasets in every database table
@@ -24,10 +30,22 @@ export function deleteAllTables() {
/**
* Insert default datasets in the database tables
*/
export function prepopulateDatabase() {
export async function prepopulateDatabase() {
AccountRole.bulkCreate(accountRoles.data)
// Account & Sub tables
for (let account of accounts.data) {
await Account.create(account)
.then(dataset => {
Address.bulkCreate(account.addresses)
Payment.bulkCreate(account.payments)
})
}
Category.bulkCreate(categories.data)
Brand.bulkCreate(brands.data)
Product.bulkCreate(products.data)
Account.bulkCreate(accounts.data)
Order.bulkCreate(orders.data)
OrderItem.bulkCreate(orderItems.data)
}

View File

@@ -10,8 +10,7 @@ defineProps({
type: String
},
subtitle: {
type: String,
default: ""
type: String
}
})
</script>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ModelRef } from 'vue';
import actionDialog from './actionDialog.vue';
import outlinedButton from './outlinedButton.vue';
const showDialog: ModelRef<boolean> = defineModel()
@@ -31,21 +32,19 @@ function confirmPressed() {
</v-container>
<template #actions>
<v-btn
<outlined-button
@click="showDialog = false"
color="green"
variant="outlined"
>
{{ $t("dialog.cancel") }}
</v-btn>
</outlined-button>
<v-btn
<outlined-button
@click="confirmPressed"
color="red"
variant="outlined"
>
{{ $t("dialog.confirm") }}
</v-btn>
</outlined-button>
</template>
</action-dialog>
</template>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
defineProps({
prependIcon: String,
color: {
type: String,
default: "primary"
}
})
</script>
<template>
<v-btn
:prepend-icon="prependIcon"
variant="outlined"
:color="color"
>
<slot></slot>
</v-btn>
</template>

View File

@@ -1,7 +1,16 @@
import axios from "axios"
import { OrderModel } from "../models/orderModel"
import { BasketItemModel } from "../models/basketItemModel"
const BASE_URL = "http://localhost:3000/orders"
export async function getUserOrders(userId: number) {
return axios.get(BASE_URL + "/" + userId)
}
export async function addOrder(accountId: number, basketItems: Array<BasketItemModel>) {
return axios.post(BASE_URL, {
"accountId": accountId,
"orderItem": basketItems
})
}

View File

@@ -1,5 +1,5 @@
export class BasketItemModel {
productId: number = -1
id: number = -1
brand: string = ""
name: string = ""
categoryName: string = ""

View File

@@ -4,6 +4,9 @@ import { calcProductPrice } from "@/scripts/productScripts";
import { BasketItemModel } from "../models/basketItemModel";
import { useFeedbackStore } from "./feedbackStore";
import { BannerStateEnum } from "../enums/bannerStateEnum";
import { OrderModel } from "../models/orderModel";
import { useUserStore } from "./userStore";
import { addOrder } from "../api/orderApi";
export const useBasketStore = defineStore('basket', {
state: () => ({
@@ -42,6 +45,18 @@ export const useBasketStore = defineStore('basket', {
} else {
this.itemsInBasket.push(item)
}
},
takeOrder() {
const userStore = useUserStore()
//
// const order = new OrderModel()
// order.accountId = userStore.userAccount.id
// order.orderItem = this.itemsInBasket
//
// console.log(order)
addOrder(userStore.userAccount.id, this.itemsInBasket)
}
}
})

View File

@@ -46,7 +46,8 @@
"ordered": "Bestellt",
"preparingForShipping": "Versandvorbereitung",
"shipped": "Versendet",
"delivered": "Geliefert"
"delivered": "Geliefert",
"inDelivery": "In Zustellung"
},
"userInfo": {
"firstName": "Vorname",
@@ -90,7 +91,8 @@
},
"oclock": "Uhr",
"ordering": {
"ordering": "Bestellabschluss"
"ordering": "Bestellabschluss",
"takeOrder": "Bestellung ausführen"
},
"dialog": {
"cancel": "Abbrechen",

View File

@@ -46,7 +46,8 @@
"ordered": "Ordered",
"preparingForShipping": "Preparing for shipping",
"shipped": "Shipped",
"delivered": "Delivered"
"delivered": "Delivered",
"inDelivery": "In Delivery"
},
"userInfo": {
"firstName": "First Name",
@@ -90,7 +91,8 @@
},
"oclock": "o'clock",
"ordering": {
"ordering": "Finish order"
"ordering": "Finish order",
"takeOrder": "Do order now"
},
"dialog": {
"cancel": "Cancel",

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { useUserStore } from '@/data/stores/userStore';
import cardView from '@/components/cardView.vue';
import outlinedButton from '@/components/outlinedButton.vue';
const userStore = useUserStore()
</script>
@@ -87,14 +88,13 @@ const userStore = useUserStore()
</v-container>
<template #actions>
<v-btn
<outlined-button
@click="userStore.updateAccount()"
variant="outlined"
prepend-icon="mdi-content-save"
color="green"
>
Save
</v-btn>
</outlined-button>
</template>
</card-view>
</template>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import cardView from '@/components/cardView.vue';
import confirmDialog from '@/components/confirmDialog.vue';
import outlinedButton from '@/components/outlinedButton.vue';
import { ref } from 'vue';
const showConfirmDialog = ref(false)
@@ -15,14 +16,13 @@ function deleteAccount() {
<v-container>
<v-row>
<v-col class="d-flex justify-center align-center">
<v-btn
<outlined-button
prepend-icon="mdi-delete"
variant="outlined"
color="red"
@click="showConfirmDialog = true"
>
{{ $t("account.delete") }}
</v-btn>
</outlined-button>
</v-col>
</v-row>
</v-container>

View File

@@ -5,6 +5,7 @@ import alertBanner from '@/components/alertBanner.vue';
import cardView from '@/components/cardView.vue';
import { useUserStore } from '@/data/stores/userStore';
import orderingDialog from './orderingDialog.vue';
import outlinedButton from '@/components/outlinedButton.vue';
import { ref } from 'vue';
const basketStore = useBasketStore()
@@ -44,7 +45,7 @@ const showOrderingDialog = ref()
</v-card-text>
<template #actions>
<v-btn
<outlined-button
prepend-icon="mdi-basket-check"
:disabled="basketStore.itemsInBasket.length == 0 || userStore.userAccount.id == null"
variant="outlined"
@@ -52,7 +53,7 @@ const showOrderingDialog = ref()
@click="showOrderingDialog = true"
>
{{ $t('orderNow') }}
</v-btn>
</outlined-button>
</template>
</card-view>
</v-col>

View File

@@ -1,9 +1,25 @@
<script setup lang="ts">
import actionDialog from '@/components/actionDialog.vue';
import { useBasketStore } from '@/data/stores/basketStore';
import outlinedButton from '@/components/outlinedButton.vue';
const basketStore = useBasketStore()
function doOrder() {
basketStore.takeOrder()
}
</script>
<template>
<action-dialog
:title="$t('ordering.ordering')"
/>
>
<template #actions>
<outlined-button
@click="doOrder"
>
{{ $t('ordering.takeOrder') }}
</outlined-button>
</template>
</action-dialog>
</template>

View File

@@ -2,6 +2,7 @@
import { useUserStore } from '@/data/stores/userStore';
import { ref } from 'vue';
import cardView from '@/components/cardView.vue';
import outlinedButton from '@/components/outlinedButton.vue';
const userStore = useUserStore()
const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false })
@@ -31,13 +32,19 @@ function startLogin() {
</v-container>
<template #actions>
<v-btn variant="outlined" @click="showRegisterCard = true" color="primary" prepend-icon="mdi-plus">
<outlined-button
@click="showRegisterCard = true"
prepend-icon="mdi-plus"
>
{{ $t('account.noAccountRegister') }}
</v-btn>
<v-spacer />
<v-btn variant="outlined" append-icon="mdi-arrow-right" color="primary"
@click="startLogin">{{ $t('menu.login') }}</v-btn>
</template>
</outlined-button>
<outlined-button
append-icon="mdi-arrow-right"
@click="startLogin"
>
{{ $t('menu.login') }}
</outlined-button>
</template>
</card-view>
</template>

View File

@@ -3,6 +3,7 @@ import { AccountModel } from '@/data/models/accountModel';
import { useUserStore } from '@/data/stores/userStore';
import { ref } from 'vue';
import cardView from '@/components/cardView.vue';
import outlinedButton from '@/components/outlinedButton.vue';
const newUser = ref(new AccountModel())
const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false })
@@ -78,15 +79,19 @@ const userStore = useUserStore()
</v-container>
<template #actions>
<v-btn prepend-icon="mdi-arrow-left" color="primary" variant="outlined"
@click="showRegisterCard = false">
{{ $t('account.backToLogin') }}
</v-btn>
<v-spacer />
<v-btn prepend-icon="mdi-account-plus" color="primary" variant="outlined"
@click="userStore.registerAccount(newUser)">
{{ $t('account.register') }}
</v-btn>
<outlined-button
prepend-icon="mdi-arrow-left"
@click="showRegisterCard = false"
>
{{ $t('account.backToLogin') }}
</outlined-button>
<outlined-button
prepend-icon="mdi-account-plus"
@click="userStore.registerAccount(newUser)"
>
{{ $t('account.register') }}
</outlined-button>
</template>
</card-view>
</template>

View File

@@ -6,13 +6,13 @@ defineProps({
order: OrderModel
})
function getDotColor(order, step: number) {
if (order.shippingProgress == step)
{
return "orange"
} else if (order.shippingProgress >= step)
function getDotColor(order: OrderModel, step: number) {
if (order.shippingProgress >= step)
{
return "green"
} else if (order.shippingProgress + 1 == step)
{
return "blue"
} else
{
return "grey"
@@ -32,7 +32,7 @@ function formatDateTimeString(string: string) {
:title="$t('orders.orderFrom') + ' ' + formatDateTimeString(order.createdAt) + ' ' + $t('oclock')"
:subtitle="$t('totalPrice') + ': ' + order.totalPrice + ' €'"
>
<v-timeline direction="horizontal" side="start">
<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>
@@ -41,11 +41,15 @@ function formatDateTimeString(string: string) {
{{ $t('orders.preparingForShipping') }}
</v-timeline-item>
<v-timeline-item :dot-color="getDotColor(order, 3)" icon="mdi-truck-fast">
<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-package-check">
<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>

View File

@@ -3,7 +3,7 @@ import { BannerStateEnum } from '@/data/enums/bannerStateEnum';
import { useFeedbackStore } from '@/data/stores/feedbackStore';
import axios from 'axios';
import cardView from '@/components/cardView.vue';
import actionDialog from '@/components/actionDialog.vue';
import outlinedButton from '@/components/outlinedButton.vue';
import { ref } from 'vue';
import confirmDialog from '@/components/confirmDialog.vue';
@@ -18,7 +18,7 @@ function resetDb() {
}
})
confirmDialog.value = false
showConfirmDialog.value = false
// todo: Request all data
}
@@ -32,24 +32,21 @@ function resetSettings() {
<v-container>
<v-row>
<v-col class="d-flex justify-center align-center">
<v-btn
<outlined-button
@click="showConfirmDialog = true"
prepend-icon="mdi-database-refresh"
color="red"
variant="outlined"
>
{{ $t('preferences.resetDatabase') }}
</v-btn>
</outlined-button>
</v-col>
<v-col class="d-flex justify-center align-center">
<v-btn
@click="resetDb"
<outlined-button
@click="resetSettings"
prepend-icon="mdi-cog-counterclockwise"
color="primary"
variant="outlined"
>
{{ $t('preferences.resetPreferences') }}
</v-btn>
</outlined-button>
</v-col>
</v-row>
</v-container>
@@ -59,5 +56,6 @@ function resetSettings() {
:title="$t('dialog.resetConfirm.title')"
:description="$t('dialog.resetConfirm.description')"
v-model="showConfirmDialog"
:onConfirm="resetDb"
/>
</template>

View File

@@ -5,7 +5,7 @@ import { useBasketStore } from '@/data/stores/basketStore';
import { calcProductPrice, productToBasketItem } from '@/scripts/productScripts';
import ActionDialog from '@/components/actionDialog.vue'
import { ProductWithCategoryModel } from '@/data/models/productWithCategoryModel';
import { BasketItemModel } from '@/data/models/basketItemModel';
import outlinedButton from '@/components/outlinedButton.vue';
const props = defineProps({
product: {
@@ -20,9 +20,6 @@ const basketStore = useBasketStore()
const selectedImage = ref("")
function addProductToBasket() {
basketStore.addItemToBasket(
new BasketItemModel()
)
basketStore.addItemToBasket(productToBasketItem(props.product, nrOfArticles.value))
nrOfArticles.value = 1
showDialog.value = false
@@ -67,7 +64,6 @@ watch(() => props.product.images, () => {
<v-spacer />
</v-row>
</v-col>
@@ -154,16 +150,14 @@ watch(() => props.product.images, () => {
:disabled="product.storedItems == 0"
/>
<v-btn
<outlined-button
prepend-icon="mdi-cart-plus"
@click="addProductToBasket"
color="primary"
variant="outlined"
height="50"
:disabled="product.storedItems == 0"
>
{{ $t('addToBasket') }}
</v-btn>
</outlined-button>
</template>
</action-dialog>
</template>