Files
SkyArtShop/website/public/home.html
Local Server 2db9f83d2d Performance: Optimize database and frontend-backend communication
Major optimizations implemented:

DATABASE:
- Added 6 new composite indexes for products queries
- Added slug index for blogposts and products
- Added composite index for portfolio active + display order
- ANALYZE all tables to update query planner statistics
- VACUUM database for optimal performance

FRONTEND API CACHING:
- Created api-cache.js with intelligent caching system
- Request deduplication for simultaneous calls
- Custom TTL per endpoint (5-30 minutes)
- Automatic cache cleanup every minute
- Cache hit/miss logging for monitoring

FRONTEND INTEGRATION:
- Updated portfolio.html to use apiCache
- Updated blog.html to use apiCache
- Updated shop.html to use apiCache
- Updated home.html to use apiCache
- Updated product.html to use apiCache (2 endpoints)

PERFORMANCE RESULTS:
- API response times: 7-12ms (excellent)
- Backend cache hit rates showing 0-41% improvement
- All endpoints returning HTTP 200
- All pages loading in under 10ms

TESTING:
- Added test-api-performance.sh for continuous monitoring
- Verified all 6 API endpoints functional
- Verified all frontend pages loading correctly
- Database indexes verified (30+ indexes active)

No functionality changes - pure performance optimization.
2026-01-14 08:19:20 -06:00

603 lines
23 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Sky Art Shop - Scrapbooking, journaling, cardmaking, and collaging stationery."
/>
<title>Home - Sky Art Shop</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
/>
<link rel="stylesheet" href="/assets/css/theme-colors.css" />
<link rel="stylesheet" href="/assets/css/main.css?v=1735692100" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1767233028" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1736790001" />
<link rel="stylesheet" href="/assets/css/cart-wishlist.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
<link rel="stylesheet" href="/assets/css/responsive.css" />
<link rel="stylesheet" href="/assets/css/navbar-mobile-fix.css?v=1736790000" />
</head>
<body>
<script>window.__bodyReady=true</script>
<!-- Sticky Banner Wrapper -->
<div class="sticky-banner-wrapper">
<!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home" class="brand-link">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop Logo"
class="brand-logo"
/>
<span class="brand-name">Sky' Art Shop</span>
</a>
</div>
<div class="navbar-menu">
<ul class="nav-menu-list">
<li class="nav-item">
<a href="/home" class="nav-link">Home</a>
</li>
<li class="nav-item">
<a href="/shop" class="nav-link">Shop</a>
</li>
<li class="nav-item">
<a href="/portfolio" class="nav-link">Portfolio</a>
</li>
<li class="nav-item">
<a href="/about" class="nav-link">About</a>
</li>
<li class="nav-item">
<a href="/blog" class="nav-link">Blog</a>
</li>
<li class="nav-item">
<a href="/contact" class="nav-link">Contact</a>
</li>
</ul>
</div>
<div class="navbar-actions">
<div class="action-item wishlist-dropdown-wrapper">
<button
class="action-btn"
id="wishlistToggle"
aria-label="Wishlist"
>
<i class="bi bi-heart"></i>
<span class="action-badge" id="wishlistCount">0</span>
</button>
<div class="action-dropdown wishlist-dropdown" id="wishlistPanel">
<div class="dropdown-head">
<h3>My Wishlist</h3>
<button class="dropdown-close" id="wishlistClose">
<i class="bi bi-x-lg"></i>
</button>
</div>
<div class="dropdown-body" id="wishlistContent">
<p class="empty-state">Your wishlist is empty</p>
</div>
<div class="dropdown-foot">
<a href="/shop" class="btn-outline">Continue Shopping</a>
</div>
</div>
</div>
<div class="action-item cart-dropdown-wrapper">
<button
class="action-btn"
id="cartToggle"
aria-label="Shopping Cart"
>
<i class="bi bi-cart3"></i>
<span class="action-badge" id="cartCount">0</span>
</button>
<div class="action-dropdown cart-dropdown" id="cartPanel">
<div class="dropdown-head">
<h3>Shopping Cart</h3>
<button class="dropdown-close" id="cartClose">
<i class="bi bi-x-lg"></i>
</button>
</div>
<div class="dropdown-body" id="cartContent">
<p class="empty-state"><i class="bi bi-cart-x"></i><br>Your cart is empty</p>
</div>
<div class="dropdown-foot">
<a href="/shop" class="btn-outline">Continue Shopping</a>
</div>
</div>
</div>
<button class="mobile-toggle" id="mobileMenuToggle" aria-label="Menu">
<span class="toggle-line"></span>
<span class="toggle-line"></span>
<span class="toggle-line"></span>
</button>
</div>
</div>
<div class="mobile-menu" id="mobileMenu">
<div class="mobile-menu-header">
<span class="mobile-brand">Sky' Art Shop</span>
<button class="mobile-close" id="mobileMenuClose">
<i class="bi bi-x-lg"></i>
</button>
</div>
<ul class="mobile-menu-list">
<li><a href="/home" class="mobile-link">Home</a></li>
<li><a href="/shop" class="mobile-link">Shop</a></li>
<li><a href="/portfolio" class="mobile-link">Portfolio</a></li>
<li><a href="/about" class="mobile-link">About</a></li>
<li><a href="/blog" class="mobile-link">Blog</a></li>
<li><a href="/contact" class="mobile-link">Contact</a></li>
</ul>
</div>
<div class="mobile-menu-overlay" id="mobileMenuOverlay"></div>
</nav>
<script>
// Mobile Menu Toggle
(function() {
const mobileToggle = document.getElementById('mobileMenuToggle');
const mobileMenu = document.getElementById('mobileMenu');
const mobileClose = document.getElementById('mobileMenuClose');
const overlay = document.getElementById('mobileMenuOverlay');
function openMenu() {
mobileMenu.classList.add('active');
overlay.classList.add('active');
document.body.style.overflow = 'hidden';
}
function closeMenu() {
mobileMenu.classList.remove('active');
overlay.classList.remove('active');
document.body.style.overflow = '';
}
if (mobileToggle) mobileToggle.addEventListener('click', openMenu);
if (mobileClose) mobileClose.addEventListener('click', closeMenu);
if (overlay) overlay.addEventListener('click', closeMenu);
// Close on ESC key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) {
closeMenu();
}
});
})();
</script>
<!-- Hero Section -->
<section class="hero" id="heroSection">
<div class="hero-content" id="heroContent">
<h2 id="heroHeadline">Welcome to Sky Art Shop</h2>
<p id="heroSubheading">
Your destination for creative stationery and supplies
</p>
<div class="hero-description" id="heroDescription">
<p>
Discover our curated collection of scrapbooking, journaling,
cardmaking, and collaging supplies. Express your creativity and
bring your artistic vision to life.
</p>
</div>
<a href="/shop" class="btn btn-primary" id="heroCtaBtn">Shop Now</a>
</div>
<div class="hero-image" id="heroImageContainer">
<img
src="/assets/images/hero-image.jpg"
alt="Sky Art Shop"
loading="lazy"
onerror="this.style.display='none'"
/>
</div>
</section>
<!-- Promotion/Inspiration Section -->
<section class="inspiration" id="promotionSection">
<div class="container">
<h2 id="promotionTitle">Get Inspired</h2>
<div class="inspiration-content" id="promotionContent">
<div class="inspiration-text" id="promotionText">
<p>
At Sky Art Shop, we believe in the power of creativity to
transform and inspire. Whether you're an experienced crafter or
just beginning your creative journey, we have everything you need
to bring your ideas to life.
</p>
<p>
Explore our collection of washi tapes, stickers, stamps, and more.
Each item is carefully selected to help you create something
beautiful and meaningful.
</p>
</div>
<div class="inspiration-image" id="promotionImage">
<img
src="/assets/images/inspiration.jpg"
alt="Creative Inspiration"
loading="lazy"
onerror="this.style.display='none'"
/>
</div>
</div>
<a href="/portfolio" class="btn btn-secondary">View Portfolio</a>
</div>
</section>
<!-- Featured Products / Portfolio Section -->
<section class="collection" id="portfolioSection">
<div class="container">
<h2 id="portfolioTitle">Featured Products</h2>
<p class="section-subtitle" id="portfolioDescription">
Discover our most popular items
</p>
<div class="products-grid" id="featuredProducts">
<div class="product-card">
<div class="product-image">
<img
src="/assets/images/placeholder.svg"
alt="Product"
loading="lazy"
/>
</div>
<h3>Loading products...</h3>
</div>
</div>
<div style="margin-top: 40px">
<a href="/shop" class="btn btn-secondary">View All Products</a>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title" id="footerSiteName">Sky Art Shop</h3>
<p class="footer-text">
Your destination for unique art pieces and creative supplies.
</p>
<div class="social-links">
<a href="#" class="social-link"><i class="bi bi-facebook"></i></a>
<a href="#" class="social-link"
><i class="bi bi-instagram"></i
></a>
<a href="#" class="social-link"><i class="bi bi-twitter"></i></a>
<a href="#" class="social-link"
><i class="bi bi-pinterest"></i
></a>
</div>
</div>
<div class="footer-col">
<h4 class="footer-heading">Shop</h4>
<ul class="footer-links">
<li><a href="/shop">All Products</a></li>
<li><a href="/shop?category=paintings">Paintings</a></li>
<li><a href="/shop?category=prints">Prints</a></li>
<li><a href="/shop?category=supplies">Art Supplies</a></li>
</ul>
</div>
<div class="footer-col">
<h4 class="footer-heading">About</h4>
<ul class="footer-links">
<li><a href="/about">Our Story</a></li>
<li><a href="/portfolio">Portfolio</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<div class="footer-col">
<h4 class="footer-heading">Customer Service</h4>
<ul class="footer-links">
<li><a href="/shipping-info">Shipping Info</a></li>
<li><a href="/returns">Returns</a></li>
<li><a href="/faq">FAQ</a></li>
<li><a href="/privacy">Privacy Policy</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p id="footerText">&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/assets/js/api-cache.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/shop-system.js"></script>
<script src="/assets/js/page-transitions.js?v=1766709739"></script>
<script src="/assets/js/back-button-control.js?v=1766723554"></script>
<script>
// Load homepage settings
async function loadHomepageSettings() {
try {
const response = await fetch("/api/homepage/settings");
if (response.ok) {
const data = await response.json();
if (data.success && data.settings) {
applyHomepageSettings(data.settings);
}
}
} catch (error) {
console.log("Using default homepage settings");
}
}
function applyHomepageSettings(settings) {
// Apply Hero Section
if (settings.hero) {
const heroSection = document.getElementById("heroSection");
const heroContent = document.getElementById("heroContent");
if (!settings.hero.enabled) {
heroSection.style.display = "none";
return;
}
if (settings.hero.headline) {
document.getElementById("heroHeadline").textContent =
settings.hero.headline;
}
if (settings.hero.subheading) {
document.getElementById("heroSubheading").textContent =
settings.hero.subheading;
}
if (settings.hero.description) {
document.getElementById("heroDescription").innerHTML =
settings.hero.description;
}
if (settings.hero.ctaText && settings.hero.ctaLink) {
const ctaBtn = document.getElementById("heroCtaBtn");
ctaBtn.textContent = settings.hero.ctaText;
ctaBtn.href = settings.hero.ctaLink;
}
if (settings.hero.backgroundUrl) {
const isVideo =
settings.hero.backgroundUrl.match(/\.(mp4|webm|ogg)$/i);
const heroImageContainer =
document.getElementById("heroImageContainer");
if (isVideo) {
heroImageContainer.innerHTML = `
<video autoplay muted loop playsinline style="width: 100%; height: 100%; object-fit: cover;">
<source src="${settings.hero.backgroundUrl}" type="video/mp4">
</video>
`;
} else {
heroImageContainer.innerHTML = `<img src="${settings.hero.backgroundUrl}" alt="Hero Background" loading="lazy" />`;
}
}
// Apply layout
if (settings.hero.layout) {
heroContent.style.textAlign = settings.hero.layout.replace(
"text-",
""
);
}
}
// Apply Promotion Section
if (settings.promotion) {
const promotionSection = document.getElementById("promotionSection");
if (!settings.promotion.enabled) {
promotionSection.style.display = "none";
} else {
if (settings.promotion.title) {
document.getElementById("promotionTitle").textContent =
settings.promotion.title;
}
if (settings.promotion.description) {
document.getElementById("promotionText").innerHTML =
settings.promotion.description;
}
if (settings.promotion.imageUrl) {
const promotionImage = document.getElementById("promotionImage");
promotionImage.innerHTML = `<img src="${
settings.promotion.imageUrl
}" alt="${
settings.promotion.title || "Promotion"
}" loading="lazy" />`;
}
// Apply text alignment
if (settings.promotion.textAlignment) {
document.getElementById("promotionText").style.textAlign =
settings.promotion.textAlignment;
}
// Apply image position (you can customize CSS classes for this)
const promotionContent =
document.getElementById("promotionContent");
if (settings.promotion.imagePosition === "right") {
promotionContent.style.flexDirection = "row-reverse";
} else if (settings.promotion.imagePosition === "center") {
promotionContent.style.flexDirection = "column";
}
}
}
// Apply Portfolio Section
if (settings.portfolio) {
const portfolioSection = document.getElementById("portfolioSection");
if (!settings.portfolio.enabled) {
portfolioSection.style.display = "none";
} else {
if (settings.portfolio.title) {
document.getElementById("portfolioTitle").textContent =
settings.portfolio.title;
}
if (settings.portfolio.description) {
const descEl = document.getElementById("portfolioDescription");
if (descEl) {
descEl.innerHTML = settings.portfolio.description;
}
}
// Portfolio count is handled by existing featured products logic
}
}
}
// Load site settings
async function loadSiteSettings() {
try {
const response = await fetch("/api/settings");
if (response.ok) {
const data = await response.json();
if (data.settings) {
document.getElementById("siteName").textContent =
data.settings.sitename || "Sky Art Shop";
document.getElementById("footerSiteName").textContent =
data.settings.sitename || "Sky Art Shop";
document.getElementById("footerText").textContent =
data.settings.footertext ||
"© 2025 by Sky Art Shop. All rights reserved.";
}
}
} catch (error) {
console.log("Using default site settings");
}
}
// Load featured products
async function loadFeaturedProducts() {
try {
const response = await window.apiCache.fetch("/api/products/featured?limit=4");
if (response.ok) {
const data = await response.json();
if (data.products && data.products.length > 0) {
const container = document.getElementById("featuredProducts");
container.innerHTML = data.products
.map((product) => {
// Get product image (primary or first from images array)
let productImage = "/assets/images/placeholder.svg";
if (
product.images &&
Array.isArray(product.images) &&
product.images.length > 0
) {
const primaryImg = product.images.find(
(img) => img.is_primary
);
productImage = primaryImg
? primaryImg.image_url
: product.images[0].image_url;
} else if (product.imageurl) {
productImage = product.imageurl;
}
return `
<div class="product-card">
<a href="/product?id=${
product.id
}" class="product-link">
<div class="product-image">
<img src="${productImage}" alt="${
product.name
}" loading="lazy" onerror="this.src='/assets/images/placeholder.svg'" />
</div>
</a>
<div class="product-info">
<a href="/product?id=${
product.id
}" class="product-title-link">
<h3>${product.name}</h3>
</a>
${
product.shortdescription ||
product.description
? `<div class="product-description">${
product.shortdescription ||
(product.description
? product.description.substring(
0,
100
) + "..."
: "")
}</div>`
: ""
}
<p class="price">$${parseFloat(
product.price
).toFixed(2)}</p>
</div>
<div class="product-actions">
<button class="btn btn-small btn-icon" onclick="addToWishlist('${
product.id
}', '${product.name}', ${
product.price
}, '${productImage}')" aria-label="Add to wishlist">
<i class="bi bi-heart"></i>
</button>
<button class="btn btn-small btn-icon" onclick="addToCart('${
product.id
}', '${product.name}', ${
product.price
}, '${productImage}')" aria-label="Add to cart">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
`;
})
.join("");
}
}
} catch (error) {
console.log("Could not load featured products");
}
}
// Cart and Wishlist Functions
function addToCart(productId, name, price, imageurl) {
window.ShopSystem.addToCart(
{
id: String(productId),
name,
price: parseFloat(price),
imageurl,
},
1
);
}
function addToWishlist(productId, name, price, imageurl) {
window.ShopSystem.addToWishlist({
id: String(productId),
name,
price: parseFloat(price),
imageurl,
});
}
// Initialize
loadSiteSettings();
loadHomepageSettings();
loadFeaturedProducts();
</script>
</body>
</html>