Files
Kikomiko/kikomiko/dist/js/cart.js
T

102 lines
2.4 KiB
JavaScript

/** @typedef {{ productId: string; slug: string; name: string; price: number; image?: string | null; category?: string | null; quantity: number }} CartItem */
const STORAGE_KEY = "kikomiko-cart-v1";
/** @type {CartItem[]} */
let items = [];
let hydrated = false;
const listeners = new Set();
function notify() {
for (const fn of listeners) fn(getSnapshot());
}
function persist() {
if (!hydrated) return;
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
} catch {
/* ignore */
}
}
export function hydrateCart() {
if (hydrated) return;
try {
const raw = localStorage.getItem(STORAGE_KEY);
items = raw ? JSON.parse(raw) : [];
if (!Array.isArray(items)) items = [];
} catch {
items = [];
}
hydrated = true;
notify();
}
export function subscribe(fn) {
listeners.add(fn);
return () => listeners.delete(fn);
}
export function getSnapshot() {
const itemCount = items.reduce((n, i) => n + i.quantity, 0);
const subtotal = items.reduce((n, i) => n + i.price * i.quantity, 0);
return { items: [...items], itemCount, subtotal, hydrated };
}
export function addItem(item) {
hydrateCart();
const qty = Math.max(1, item.quantity ?? 1);
const existing = items.find((i) => i.productId === item.productId);
if (existing) {
existing.quantity += qty;
if (item.category && !existing.category) existing.category = item.category;
if (item.image && !existing.image) existing.image = item.image;
} else {
items.push({
productId: item.productId,
slug: item.slug,
name: item.name,
price: item.price,
image: item.image ?? null,
category: item.category ?? null,
quantity: qty,
});
}
persist();
notify();
}
export function updateQuantity(productId, quantity) {
hydrateCart();
if (quantity <= 0) {
items = items.filter((i) => i.productId !== productId);
} else {
const row = items.find((i) => i.productId === productId);
if (row) row.quantity = quantity;
}
persist();
notify();
}
export function removeItem(productId) {
hydrateCart();
items = items.filter((i) => i.productId !== productId);
persist();
notify();
}
export function clearCart() {
hydrateCart();
items = [];
persist();
notify();
}
export function formatPrice(price) {
const amount = Number.isFinite(price) ? Math.round(price) : 0;
return `${new Intl.NumberFormat("fa-IR", {
maximumFractionDigits: 0,
}).format(amount)} تومان`;
}