feature/book-page (#4)

Co-authored-by: Tim Rijkse <trijkse@gmail.com>
Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
2026-01-16 08:46:03 +00:00
parent 45d0872495
commit 3dbe404443
30 changed files with 2534 additions and 384 deletions

158
js/store/cart.js Normal file
View File

@@ -0,0 +1,158 @@
/**
* Cart Store
* Manages shopping cart state with localStorage persistence
* Dispatches 'cart-updated' events when cart changes
*/
const CART_STORAGE_KEY = "milinda-cart";
class CartStore {
constructor() {
this.items = this.loadFromStorage();
}
/**
* Load cart from localStorage
*/
loadFromStorage() {
try {
const stored = localStorage.getItem(CART_STORAGE_KEY);
return stored ? JSON.parse(stored) : [];
} catch (e) {
console.error("Failed to load cart from storage:", e);
return [];
}
}
/**
* Save cart to localStorage
*/
saveToStorage() {
try {
localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(this.items));
} catch (e) {
console.error("Failed to save cart to storage:", e);
}
}
/**
* Dispatch cart-updated event
*/
notifyUpdate() {
window.dispatchEvent(
new CustomEvent("cart-updated", {
detail: {
items: this.items,
count: this.getItemCount(),
total: this.getTotal(),
},
})
);
}
/**
* Add item to cart
* @param {Object} item - Item to add { title, author, price, type, image }
*/
addItem(item) {
// Check if item already exists (by title and type)
const existingIndex = this.items.findIndex(
(i) => i.title === item.title && i.type === item.type
);
if (existingIndex >= 0) {
// Increment quantity
this.items[existingIndex].quantity += 1;
} else {
// Add new item
this.items.push({
...item,
quantity: 1,
addedAt: Date.now(),
});
}
this.saveToStorage();
this.notifyUpdate();
return this.items;
}
/**
* Remove item from cart
* @param {number} index - Index of item to remove
*/
removeItem(index) {
if (index >= 0 && index < this.items.length) {
this.items.splice(index, 1);
this.saveToStorage();
this.notifyUpdate();
}
return this.items;
}
/**
* Update item quantity
* @param {number} index - Index of item
* @param {number} quantity - New quantity
*/
updateQuantity(index, quantity) {
if (index >= 0 && index < this.items.length) {
if (quantity <= 0) {
this.removeItem(index);
} else {
this.items[index].quantity = quantity;
this.saveToStorage();
this.notifyUpdate();
}
}
return this.items;
}
/**
* Clear all items from cart
*/
clear() {
this.items = [];
this.saveToStorage();
this.notifyUpdate();
return this.items;
}
/**
* Get all items
*/
getItems() {
return this.items;
}
/**
* Get total item count (sum of quantities)
*/
getItemCount() {
return this.items.reduce((sum, item) => sum + item.quantity, 0);
}
/**
* Get cart total price
*/
getTotal() {
return this.items.reduce((sum, item) => {
// Parse price like "€ 24,95" to number
const priceStr = item.price || "0";
const price = parseFloat(
priceStr.replace(/[€$£\s]/g, "").replace(",", ".")
);
return sum + price * item.quantity;
}, 0);
}
}
// Create singleton instance
const cart = new CartStore();
// Export for module use
export default cart;
// Also attach to window for global access
window.cartStore = cart;