wip: shopping cart, product options

This commit is contained in:
Nikita Kiselev
2025-07-22 23:07:10 +03:00
parent 626ee6ecb0
commit db18f3ae16
21 changed files with 429 additions and 186 deletions

View File

@@ -1,66 +1,102 @@
import {defineStore} from "pinia";
import md5 from 'crypto-js/md5';
import ftch from "@/utils/ftch.js";
import {$fetch} from "ofetch";
import {isNotEmpty} from "@/helpers.js";
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
products: [],
productsCount: 0,
total: 0,
isLoading: false,
}),
actions: {
getItem(rowId) {
return this.items.find(item => item.rowId === rowId) ?? null;
},
hasItem(rowId) {
return this.getItem(rowId) !== null;
async getProducts() {
try {
this.isLoading = true;
const {data} = await ftch('cart');
this.products = data.products;
this.productsCount = data.count;
this.total = data.total;
} catch (error) {
console.error(error);
} finally {
this.isLoading = false;
}
},
async addProduct(productId, productName, price, quantity = 1, options = []) {
const rowId = this.generateRowId(productId, options);
const item = {
rowId: rowId,
productId: productId,
productName: productName,
price: price,
quantity: quantity,
options: JSON.parse(JSON.stringify(options)), // ← 💥 глубокая копия!
};
this.items.push(item);
return rowId;
},
removeItem(rowId) {
this.items.splice(this.items.indexOf(rowId), 1);
},
getQuantity(rowId) {
if (this.hasItem(rowId)) {
return this.getItem(rowId).quantity;
}
return 0;
},
setQuantity(rowId, quantity) {
this.getItem(rowId).quantity = quantity;
},
generateRowId(productId, options) {
return md5(productId + JSON.stringify(options)).toString();
},
async checkout() {
try {
this.isLoading = true;
const {data} = await ftch('checkout', null, this.items);
this.items = data;
} catch (e) {
console.error(e);
const formData = new FormData();
formData.append("product_id", productId);
formData.append("quantity", quantity);
// TODO: Add support different types of options
options.forEach((option) => {
if (option.type === "checkbox" && Array.isArray(option.value)) {
option.value.forEach(item => {
formData.append(`option[${option.product_option_id}][]`, item.product_option_value_id);
});
} else if (option.type === "radio" && isNotEmpty(option.value)) {
formData.append(`option[${option.product_option_id}]`, option.value.product_option_value_id);
} else if (option.type === "select" && isNotEmpty(option.value)) {
formData.append(`option[${option.product_option_id}]`, option.value.product_option_value_id);
} else if ((option.type === "text" || option.type === 'textarea') && isNotEmpty(option.value)) {
formData.append(`option[${option.product_option_id}]`, option.value);
}
})
const response = await $fetch('/index.php?route=checkout/cart/add', {
method: 'POST',
body: formData,
});
if (response.error) {
throw new Error(JSON.stringify(response.error));
}
await this.getProducts();
} catch (error) {
console.log(error);
throw error;
} finally {
this.isLoading = false;
}
},
async removeItem(rowId) {
try {
this.isLoading = true;
const formData = new FormData();
formData.append('key', rowId);
await $fetch('/index.php?route=checkout/cart/remove', {
method: 'POST',
body: formData,
});
await this.getProducts();
} catch (error) {
console.error(error);
} finally {
this.isLoading = false;
}
},
async setQuantity(cartId, quantity) {
try {
this.isLoading = true;
const formData = new FormData();
formData.append(`quantity[${cartId}]`, quantity);
await $fetch('/index.php?route=checkout/cart/edit', {
redirect: 'manual',
method: 'POST',
body: formData,
});
await this.getProducts();
} catch (error) {
console.log(error);
} finally {
this.isLoading = false;
}