731 lines
22 KiB
JavaScript
731 lines
22 KiB
JavaScript
/**
|
|
* Shopping Cart & Wishlist System
|
|
* Complete, simple, reliable implementation
|
|
*/
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
console.log("[ShopSystem] Loading...");
|
|
|
|
// ========================================
|
|
// UTILS - Fallback if main.js not loaded
|
|
// ========================================
|
|
if (!window.Utils) {
|
|
window.Utils = {
|
|
formatCurrency(amount) {
|
|
return new Intl.NumberFormat("en-US", {
|
|
style: "currency",
|
|
currency: "USD",
|
|
}).format(amount);
|
|
},
|
|
escapeHtml(text) {
|
|
const div = document.createElement("div");
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
},
|
|
};
|
|
}
|
|
|
|
// ========================================
|
|
// VALIDATION UTILITIES
|
|
// ========================================
|
|
const ValidationUtils = {
|
|
validateProduct(product) {
|
|
if (!product || !product.id) {
|
|
return { valid: false, error: "Invalid product: missing ID" };
|
|
}
|
|
|
|
const price = parseFloat(product.price);
|
|
if (isNaN(price) || price < 0) {
|
|
return { valid: false, error: "Invalid product price" };
|
|
}
|
|
|
|
return { valid: true, price };
|
|
},
|
|
|
|
sanitizeProduct(product, price) {
|
|
return {
|
|
id: product.id,
|
|
name: product.name || product.title || "Product",
|
|
price: price,
|
|
imageurl:
|
|
product.imageurl || product.imageUrl || product.image_url || "",
|
|
};
|
|
},
|
|
|
|
validateQuantity(quantity) {
|
|
return Math.max(1, parseInt(quantity) || 1);
|
|
},
|
|
|
|
sanitizeItems(items, includeQuantity = false) {
|
|
return items
|
|
.filter(
|
|
(item) =>
|
|
item &&
|
|
item.id &&
|
|
typeof item.price !== "undefined" &&
|
|
(!includeQuantity || item.quantity > 0)
|
|
)
|
|
.map((item) => ({
|
|
...item,
|
|
price: parseFloat(item.price) || 0,
|
|
...(includeQuantity && {
|
|
quantity: Math.max(1, parseInt(item.quantity) || 1),
|
|
}),
|
|
}));
|
|
},
|
|
};
|
|
|
|
// ========================================
|
|
// CART & WISHLIST STATE MANAGEMENT
|
|
// ========================================
|
|
|
|
class ShopState {
|
|
constructor() {
|
|
this.cart = [];
|
|
this.wishlist = [];
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
console.log("[ShopState] Initializing...");
|
|
this.loadFromStorage();
|
|
this.updateAllBadges();
|
|
console.log(
|
|
"[ShopState] Initialized - Cart:",
|
|
this.cart.length,
|
|
"Wishlist:",
|
|
this.wishlist.length
|
|
);
|
|
}
|
|
|
|
// Load data from localStorage
|
|
loadFromStorage() {
|
|
try {
|
|
const [cartData, wishlistData] = [
|
|
localStorage.getItem("skyart_cart"),
|
|
localStorage.getItem("skyart_wishlist"),
|
|
];
|
|
|
|
// Parse and validate data
|
|
this.cart = this._parseAndValidate(cartData, "cart");
|
|
this.wishlist = this._parseAndValidate(wishlistData, "wishlist");
|
|
|
|
// Sanitize items
|
|
this.cart = ValidationUtils.sanitizeItems(this.cart, true);
|
|
this.wishlist = ValidationUtils.sanitizeItems(this.wishlist, false);
|
|
} catch (e) {
|
|
console.error("[ShopState] Load error:", e);
|
|
this._clearCorruptedData();
|
|
}
|
|
}
|
|
|
|
_parseAndValidate(data, type) {
|
|
const parsed = data ? JSON.parse(data) : [];
|
|
if (!Array.isArray(parsed)) {
|
|
console.warn(`[ShopState] Invalid ${type} data, resetting`);
|
|
return [];
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
_clearCorruptedData() {
|
|
localStorage.removeItem("skyart_cart");
|
|
localStorage.removeItem("skyart_wishlist");
|
|
this.cart = [];
|
|
this.wishlist = [];
|
|
}
|
|
|
|
// Save data to localStorage
|
|
saveToStorage() {
|
|
try {
|
|
// Check localStorage availability
|
|
if (typeof localStorage === "undefined") {
|
|
console.error("[ShopState] localStorage not available");
|
|
return false;
|
|
}
|
|
|
|
const cartJson = JSON.stringify(this.cart);
|
|
const wishlistJson = JSON.stringify(this.wishlist);
|
|
|
|
// Check size (5MB limit for most browsers)
|
|
if (cartJson.length + wishlistJson.length > 4000000) {
|
|
console.warn(
|
|
"[ShopState] Storage data too large, trimming old items"
|
|
);
|
|
// Keep only last 50 cart items and 100 wishlist items
|
|
this.cart = this.cart.slice(-50);
|
|
this.wishlist = this.wishlist.slice(-100);
|
|
}
|
|
|
|
localStorage.setItem("skyart_cart", JSON.stringify(this.cart));
|
|
localStorage.setItem("skyart_wishlist", JSON.stringify(this.wishlist));
|
|
return true;
|
|
} catch (e) {
|
|
console.error("[ShopState] Save error:", e);
|
|
|
|
// Handle quota exceeded error
|
|
if (e.name === "QuotaExceededError" || e.code === 22) {
|
|
console.warn("[ShopState] Storage quota exceeded, clearing old data");
|
|
// Try to recover by keeping only essential items
|
|
this.cart = this.cart.slice(-20);
|
|
this.wishlist = this.wishlist.slice(-30);
|
|
try {
|
|
localStorage.setItem("skyart_cart", JSON.stringify(this.cart));
|
|
localStorage.setItem(
|
|
"skyart_wishlist",
|
|
JSON.stringify(this.wishlist)
|
|
);
|
|
this.showNotification(
|
|
"Storage limit reached. Older items removed.",
|
|
"info"
|
|
);
|
|
} catch (retryError) {
|
|
console.error("[ShopState] Failed to recover storage:", retryError);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// CART METHODS
|
|
// ========================================
|
|
|
|
addToCart(product, quantity = 1) {
|
|
console.log("[ShopState] Adding to cart:", product);
|
|
|
|
const validation = ValidationUtils.validateProduct(product);
|
|
if (!validation.valid) {
|
|
console.error("[ShopState] Invalid product:", product);
|
|
this.showNotification(validation.error, "error");
|
|
return false;
|
|
}
|
|
|
|
quantity = ValidationUtils.validateQuantity(quantity);
|
|
const existing = this._findById(this.cart, product.id);
|
|
|
|
if (existing) {
|
|
existing.quantity = Math.min(existing.quantity + quantity, 999);
|
|
} else {
|
|
const sanitized = ValidationUtils.sanitizeProduct(
|
|
product,
|
|
validation.price
|
|
);
|
|
this.cart.push({ ...sanitized, quantity });
|
|
}
|
|
|
|
return this._saveAndUpdate(
|
|
"cart",
|
|
product.name || product.title || "Item",
|
|
"added to cart"
|
|
);
|
|
}
|
|
|
|
removeFromCart(productId) {
|
|
console.log("[ShopState] Removing from cart:", productId);
|
|
this.cart = this.cart.filter(
|
|
(item) => String(item.id) !== String(productId)
|
|
);
|
|
this.saveToStorage();
|
|
this.updateAllBadges();
|
|
this.renderCartDropdown();
|
|
this.showNotification("Item removed from cart", "info");
|
|
|
|
// Dispatch event for cart.js compatibility
|
|
window.dispatchEvent(
|
|
new CustomEvent("cart-updated", { detail: this.cart })
|
|
);
|
|
}
|
|
|
|
updateCartQuantity(productId, quantity) {
|
|
const item = this.cart.find(
|
|
(item) => String(item.id) === String(productId)
|
|
);
|
|
if (item) {
|
|
item.quantity = Math.max(1, quantity);
|
|
this.saveToStorage();
|
|
this.updateAllBadges();
|
|
this.renderCartDropdown();
|
|
|
|
// Dispatch event for cart.js compatibility
|
|
window.dispatchEvent(
|
|
new CustomEvent("cart-updated", { detail: this.cart })
|
|
);
|
|
}
|
|
}
|
|
|
|
getCartTotal() {
|
|
return this.cart.reduce((sum, item) => {
|
|
const price = parseFloat(item.price) || 0;
|
|
const quantity = parseInt(item.quantity) || 0;
|
|
return sum + price * quantity;
|
|
}, 0);
|
|
}
|
|
|
|
getCartCount() {
|
|
return this.cart.reduce((sum, item) => {
|
|
const quantity = parseInt(item.quantity) || 0;
|
|
return sum + quantity;
|
|
}, 0);
|
|
}
|
|
|
|
// ========================================
|
|
// WISHLIST METHODS
|
|
// ========================================
|
|
|
|
addToWishlist(product) {
|
|
console.log("[ShopState] Adding to wishlist:", product);
|
|
|
|
const validation = ValidationUtils.validateProduct(product);
|
|
if (!validation.valid) {
|
|
console.error("[ShopState] Invalid product:", product);
|
|
this.showNotification(validation.error, "error");
|
|
return false;
|
|
}
|
|
|
|
if (this._findById(this.wishlist, product.id)) {
|
|
this.showNotification("Already in wishlist", "info");
|
|
return false;
|
|
}
|
|
|
|
const sanitized = ValidationUtils.sanitizeProduct(
|
|
product,
|
|
validation.price
|
|
);
|
|
this.wishlist.push(sanitized);
|
|
|
|
return this._saveAndUpdate(
|
|
"wishlist",
|
|
product.name || product.title || "Item",
|
|
"added to wishlist"
|
|
);
|
|
}
|
|
|
|
removeFromWishlist(productId) {
|
|
console.log("[ShopState] Removing from wishlist:", productId);
|
|
this.wishlist = this.wishlist.filter(
|
|
(item) => String(item.id) !== String(productId)
|
|
);
|
|
this.saveToStorage();
|
|
this.updateAllBadges();
|
|
this.renderWishlistDropdown();
|
|
this.showNotification("Item removed from wishlist", "info");
|
|
|
|
// Dispatch event for cart.js compatibility
|
|
window.dispatchEvent(
|
|
new CustomEvent("wishlist-updated", { detail: this.wishlist })
|
|
);
|
|
}
|
|
|
|
isInWishlist(productId) {
|
|
return !!this._findById(this.wishlist, productId);
|
|
}
|
|
|
|
isInCart(productId) {
|
|
return !!this._findById(this.cart, productId);
|
|
}
|
|
|
|
// Helper methods
|
|
_findById(collection, id) {
|
|
return collection.find((item) => String(item.id) === String(id));
|
|
}
|
|
|
|
_saveAndUpdate(type, productName, action) {
|
|
if (!this.saveToStorage()) return false;
|
|
|
|
this.updateAllBadges();
|
|
if (type === "cart") {
|
|
this.renderCartDropdown();
|
|
} else {
|
|
this.renderWishlistDropdown();
|
|
}
|
|
|
|
this.showNotification(`${productName} ${action}`, "success");
|
|
window.dispatchEvent(
|
|
new CustomEvent(`${type}-updated`, { detail: this[type] })
|
|
);
|
|
return true;
|
|
}
|
|
|
|
// ========================================
|
|
// UI UPDATE METHODS
|
|
// ========================================
|
|
|
|
updateAllBadges() {
|
|
// Update cart badge
|
|
const cartBadge = document.getElementById("cartCount");
|
|
if (cartBadge) {
|
|
const count = this.getCartCount();
|
|
cartBadge.textContent = count;
|
|
if (count > 0) {
|
|
cartBadge.classList.add("show");
|
|
} else {
|
|
cartBadge.classList.remove("show");
|
|
}
|
|
}
|
|
|
|
// Update wishlist badge
|
|
const wishlistBadge = document.getElementById("wishlistCount");
|
|
if (wishlistBadge) {
|
|
const count = this.wishlist.length;
|
|
wishlistBadge.textContent = count;
|
|
if (count > 0) {
|
|
wishlistBadge.classList.add("show");
|
|
} else {
|
|
wishlistBadge.classList.remove("show");
|
|
}
|
|
}
|
|
}
|
|
|
|
renderCartDropdown() {
|
|
const cartContent = document.getElementById("cartContent");
|
|
if (!cartContent) return;
|
|
|
|
if (this.cart.length === 0) {
|
|
cartContent.innerHTML =
|
|
'<p class="empty-state"><i class="bi bi-cart-x"></i><br>Your cart is empty</p>';
|
|
this.updateCartFooter(0);
|
|
return;
|
|
}
|
|
|
|
cartContent.innerHTML = this.cart
|
|
.map((item) => this.createCartItemHTML(item))
|
|
.join("");
|
|
this.updateCartFooter(this.getCartTotal());
|
|
this.attachCartEventListeners();
|
|
}
|
|
|
|
createCartItemHTML(item) {
|
|
const imageUrl =
|
|
item.imageurl ||
|
|
item.imageUrl ||
|
|
item.image_url ||
|
|
"/assets/images/placeholder.jpg";
|
|
const price = parseFloat(item.price || 0).toFixed(2);
|
|
const subtotal = (parseFloat(item.price || 0) * item.quantity).toFixed(2);
|
|
|
|
return `
|
|
<div class="cart-item" data-id="${item.id}">
|
|
<img src="${imageUrl}" alt="${this.escapeHtml(
|
|
item.name
|
|
)}" class="cart-item-image" loading="lazy" onerror="this.src='/assets/images/placeholder.svg'">
|
|
<div class="cart-item-details">
|
|
<h4 class="cart-item-title">${this.escapeHtml(item.name)}</h4>
|
|
<p class="cart-item-price">$${price}</p>
|
|
<div class="cart-item-quantity">
|
|
<button class="quantity-btn quantity-minus" data-id="${item.id}">
|
|
<i class="bi bi-dash"></i>
|
|
</button>
|
|
<span class="quantity-value">${item.quantity}</span>
|
|
<button class="quantity-btn quantity-plus" data-id="${item.id}">
|
|
<i class="bi bi-plus"></i>
|
|
</button>
|
|
</div>
|
|
<p class="cart-item-subtotal">Subtotal: $${subtotal}</p>
|
|
</div>
|
|
<button class="cart-item-remove" data-id="${item.id}">
|
|
<i class="bi bi-x-lg"></i>
|
|
</button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
attachCartEventListeners() {
|
|
// Remove buttons
|
|
document.querySelectorAll(".cart-item-remove").forEach((btn) => {
|
|
btn.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
const id = e.currentTarget.dataset.id;
|
|
this.removeFromCart(id);
|
|
});
|
|
});
|
|
|
|
// Quantity minus
|
|
document.querySelectorAll(".quantity-minus").forEach((btn) => {
|
|
btn.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
const id = e.currentTarget.dataset.id;
|
|
const item = this.cart.find((i) => String(i.id) === String(id));
|
|
if (item && item.quantity > 1) {
|
|
this.updateCartQuantity(id, item.quantity - 1);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Quantity plus
|
|
document.querySelectorAll(".quantity-plus").forEach((btn) => {
|
|
btn.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
const id = e.currentTarget.dataset.id;
|
|
const item = this.cart.find((i) => String(i.id) === String(id));
|
|
if (item) {
|
|
this.updateCartQuantity(id, item.quantity + 1);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
updateCartFooter(total) {
|
|
const footer = document.querySelector("#cartPanel .dropdown-foot");
|
|
if (!footer) return;
|
|
|
|
if (total === 0 || total === null) {
|
|
footer.innerHTML =
|
|
'<a href="/shop" class="btn-outline">Continue Shopping</a>';
|
|
} else {
|
|
footer.innerHTML = `
|
|
<a href="/shop" class="btn-outline">Continue Shopping</a>
|
|
`;
|
|
}
|
|
}
|
|
|
|
renderWishlistDropdown() {
|
|
const wishlistContent = document.getElementById("wishlistContent");
|
|
if (!wishlistContent) return;
|
|
|
|
if (this.wishlist.length === 0) {
|
|
wishlistContent.innerHTML =
|
|
'<p class="empty-state"><i class="bi bi-heart"></i><br>Your wishlist is empty</p>';
|
|
return;
|
|
}
|
|
|
|
wishlistContent.innerHTML = this.wishlist
|
|
.map((item) => this.createWishlistItemHTML(item))
|
|
.join("");
|
|
this.attachWishlistEventListeners();
|
|
}
|
|
|
|
createWishlistItemHTML(item) {
|
|
const imageUrl =
|
|
item.imageurl ||
|
|
item.imageUrl ||
|
|
item.image_url ||
|
|
"/assets/images/placeholder.jpg";
|
|
const price = parseFloat(item.price || 0).toFixed(2);
|
|
|
|
return `
|
|
<div class="wishlist-item" data-id="${item.id}">
|
|
<img src="${imageUrl}" alt="${this.escapeHtml(
|
|
item.name
|
|
)}" class="wishlist-item-image" loading="lazy" onerror="this.src='/assets/images/placeholder.svg'">
|
|
<div class="wishlist-item-details">
|
|
<h4 class="wishlist-item-title">${this.escapeHtml(item.name)}</h4>
|
|
<p class="wishlist-item-price">$${price}</p>
|
|
<button class="btn-add-to-cart" data-id="${item.id}">
|
|
<i class="bi bi-cart-plus"></i> Add to Cart
|
|
</button>
|
|
</div>
|
|
<button class="wishlist-item-remove" data-id="${item.id}">
|
|
<i class="bi bi-x-lg"></i>
|
|
</button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
attachWishlistEventListeners() {
|
|
// Remove buttons
|
|
document.querySelectorAll(".wishlist-item-remove").forEach((btn) => {
|
|
btn.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
const id = e.currentTarget.dataset.id;
|
|
this.removeFromWishlist(id);
|
|
});
|
|
});
|
|
|
|
// Add to cart buttons
|
|
document.querySelectorAll(".btn-add-to-cart").forEach((btn) => {
|
|
btn.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
const id = e.currentTarget.dataset.id;
|
|
const item = this.wishlist.find((i) => String(i.id) === String(id));
|
|
if (item) {
|
|
this.addToCart(item, 1);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// ========================================
|
|
// DROPDOWN TOGGLE METHODS
|
|
// ========================================
|
|
|
|
setupDropdowns() {
|
|
// Cart dropdown
|
|
const cartToggle = document.getElementById("cartToggle");
|
|
const cartPanel = document.getElementById("cartPanel");
|
|
const cartClose = document.getElementById("cartClose");
|
|
|
|
if (cartToggle && cartPanel) {
|
|
cartToggle.addEventListener("click", () => {
|
|
cartPanel.classList.toggle("active");
|
|
this.renderCartDropdown();
|
|
});
|
|
}
|
|
|
|
if (cartClose) {
|
|
cartClose.addEventListener("click", () => {
|
|
cartPanel.classList.remove("active");
|
|
});
|
|
}
|
|
|
|
// Wishlist dropdown
|
|
const wishlistToggle = document.getElementById("wishlistToggle");
|
|
const wishlistPanel = document.getElementById("wishlistPanel");
|
|
const wishlistClose = document.getElementById("wishlistClose");
|
|
|
|
if (wishlistToggle && wishlistPanel) {
|
|
wishlistToggle.addEventListener("click", () => {
|
|
wishlistPanel.classList.toggle("active");
|
|
this.renderWishlistDropdown();
|
|
});
|
|
}
|
|
|
|
if (wishlistClose) {
|
|
wishlistClose.addEventListener("click", () => {
|
|
wishlistPanel.classList.remove("active");
|
|
});
|
|
}
|
|
|
|
// Close dropdowns when clicking outside
|
|
document.addEventListener("click", (e) => {
|
|
if (!e.target.closest(".cart-dropdown-wrapper") && cartPanel) {
|
|
cartPanel.classList.remove("active");
|
|
}
|
|
if (!e.target.closest(".wishlist-dropdown-wrapper") && wishlistPanel) {
|
|
wishlistPanel.classList.remove("active");
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========================================
|
|
// NOTIFICATION SYSTEM
|
|
// ========================================
|
|
|
|
showNotification(message, type = "info") {
|
|
// Remove existing notifications
|
|
document
|
|
.querySelectorAll(".shop-notification")
|
|
.forEach((n) => n.remove());
|
|
|
|
const notification = document.createElement("div");
|
|
notification.className = `shop-notification notification-${type}`;
|
|
notification.textContent = message;
|
|
|
|
const bgColors = {
|
|
success: "#10b981",
|
|
error: "#ef4444",
|
|
info: "#3b82f6",
|
|
};
|
|
|
|
notification.style.cssText = `
|
|
position: fixed;
|
|
top: 80px;
|
|
right: 20px;
|
|
background: ${bgColors[type] || bgColors.info};
|
|
color: white;
|
|
padding: 12px 24px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
z-index: 10000;
|
|
animation: slideInRight 0.3s ease;
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
setTimeout(() => {
|
|
notification.style.animation = "slideOutRight 0.3s ease";
|
|
setTimeout(() => notification.remove(), 300);
|
|
}, 3000);
|
|
}
|
|
|
|
// ========================================
|
|
// UTILITY METHODS
|
|
// ========================================
|
|
|
|
escapeHtml(text) {
|
|
const div = document.createElement("div");
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// INITIALIZE SYSTEM
|
|
// ========================================
|
|
|
|
// Create global instance
|
|
window.ShopSystem = new ShopState();
|
|
|
|
// ========================================
|
|
// APPSTATE COMPATIBILITY LAYER
|
|
// ========================================
|
|
// Provide AppState interface for cart.js compatibility
|
|
window.AppState = {
|
|
get cart() {
|
|
return window.ShopSystem.cart;
|
|
},
|
|
get wishlist() {
|
|
return window.ShopSystem.wishlist;
|
|
},
|
|
addToCart: (product, quantity = 1) => {
|
|
window.ShopSystem.addToCart(product, quantity);
|
|
},
|
|
removeFromCart: (productId) => {
|
|
window.ShopSystem.removeFromCart(productId);
|
|
},
|
|
updateCartQuantity: (productId, quantity) => {
|
|
window.ShopSystem.updateCartQuantity(productId, quantity);
|
|
},
|
|
getCartTotal: () => {
|
|
return window.ShopSystem.getCartTotal();
|
|
},
|
|
getCartCount: () => {
|
|
return window.ShopSystem.getCartCount();
|
|
},
|
|
addToWishlist: (product) => {
|
|
window.ShopSystem.addToWishlist(product);
|
|
},
|
|
removeFromWishlist: (productId) => {
|
|
window.ShopSystem.removeFromWishlist(productId);
|
|
},
|
|
isInWishlist: (productId) => {
|
|
return window.ShopSystem.isInWishlist(productId);
|
|
},
|
|
showNotification: (message, type) => {
|
|
window.ShopSystem.showNotification(message, type);
|
|
},
|
|
};
|
|
|
|
console.log("[ShopSystem] AppState compatibility layer installed");
|
|
|
|
// Setup dropdowns when DOM is ready
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
window.ShopSystem.setupDropdowns();
|
|
});
|
|
} else {
|
|
window.ShopSystem.setupDropdowns();
|
|
}
|
|
|
|
// Add animation styles
|
|
if (!document.getElementById("shop-system-styles")) {
|
|
const style = document.createElement("style");
|
|
style.id = "shop-system-styles";
|
|
style.textContent = `
|
|
@keyframes slideInRight {
|
|
from { transform: translateX(400px); opacity: 0; }
|
|
to { transform: translateX(0); opacity: 1; }
|
|
}
|
|
@keyframes slideOutRight {
|
|
from { transform: translateX(0); opacity: 1; }
|
|
to { transform: translateX(400px); opacity: 0; }
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
console.log("[ShopSystem] Ready!");
|
|
})();
|