webupdate

This commit is contained in:
Local Server
2026-01-18 02:22:05 -06:00
parent 6fc159051a
commit 2a2a3d99e5
135 changed files with 54897 additions and 9825 deletions

File diff suppressed because it is too large Load Diff

842
website/public/account.html Normal file
View File

@@ -0,0 +1,842 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Account | Sky Art Shop</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
/>
<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=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--color-bg-main: #ffebeb;
--color-bg-secondary: #ffd0d0;
--color-bg-promotion: #f6ccde;
--color-accent: #fcb1d8;
--color-text-main: #202023;
}
body {
font-family: "Poppins", -apple-system, BlinkMacSystemFont, sans-serif;
min-height: 100vh;
background: linear-gradient(180deg, #ffd0d0 0%, #ffebeb 100%);
position: relative;
}
/* Header */
.header {
background: var(--color-bg-secondary);
padding: 16px 32px;
box-shadow: 0 2px 12px rgba(252, 177, 216, 0.2);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 12px;
text-decoration: none;
}
.logo img {
width: 45px;
height: 45px;
object-fit: contain;
}
.logo span {
color: var(--color-text-main);
font-size: 1.25rem;
font-weight: 700;
letter-spacing: -0.3px;
}
.header-actions {
display: flex;
align-items: center;
gap: 20px;
}
.nav-link {
color: var(--color-text-main);
text-decoration: none;
font-size: 0.95rem;
font-weight: 500;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.2s;
}
.nav-link:hover {
background: rgba(255, 255, 255, 0.5);
}
.logout-btn {
background: white;
border: 2px solid #f0f0f0;
color: #ef4444;
padding: 10px 20px;
border-radius: 10px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
display: flex;
align-items: center;
gap: 6px;
}
.logout-btn:hover {
background: #fef2f2;
border-color: #ef4444;
}
/* Main Content */
.main-content {
max-width: 1200px;
margin: 0 auto;
padding: 40px 32px;
}
/* Welcome Section */
.welcome-section {
background: white;
border-radius: 24px;
padding: 36px 40px;
margin-bottom: 28px;
display: flex;
align-items: center;
gap: 24px;
box-shadow: 0 8px 32px rgba(252, 177, 216, 0.15);
}
.avatar {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #fcb1d8 0%, #f6ccde 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
color: var(--color-text-main);
font-weight: 700;
flex-shrink: 0;
}
.welcome-text h1 {
color: var(--color-text-main);
font-size: 1.6rem;
font-weight: 700;
margin-bottom: 6px;
}
.welcome-text p {
color: #666;
font-size: 0.95rem;
}
/* Stats Grid */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 28px;
}
@media (max-width: 900px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 500px) {
.stats-grid {
grid-template-columns: 1fr;
}
}
.stat-card {
background: white;
border-radius: 20px;
padding: 24px;
text-align: center;
box-shadow: 0 4px 20px rgba(252, 177, 216, 0.12);
transition: all 0.3s;
}
.stat-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(252, 177, 216, 0.25);
}
.stat-icon {
width: 52px;
height: 52px;
background: linear-gradient(
135deg,
rgba(252, 177, 216, 0.3) 0%,
rgba(246, 204, 222, 0.3) 100%
);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 14px;
font-size: 1.3rem;
color: #e85a9c;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: var(--color-text-main);
margin-bottom: 4px;
}
.stat-label {
color: #888;
font-size: 0.9rem;
font-weight: 500;
}
/* Content Grid */
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
@media (max-width: 768px) {
.content-grid {
grid-template-columns: 1fr;
}
}
.content-card {
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(252, 177, 216, 0.12);
}
.card-header {
padding: 20px 24px;
border-bottom: 1px solid #f5f5f5;
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(
135deg,
rgba(252, 177, 216, 0.08) 0%,
rgba(246, 204, 222, 0.08) 100%
);
}
.card-header h3 {
color: var(--color-text-main);
font-size: 1.05rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
}
.card-header h3 i {
color: #e85a9c;
}
.view-all {
color: #e85a9c;
text-decoration: none;
font-size: 0.85rem;
font-weight: 600;
transition: color 0.2s;
}
.view-all:hover {
color: #d14485;
}
.card-content {
padding: 24px;
}
.empty-state {
text-align: center;
padding: 40px 20px;
color: #999;
}
.empty-state i {
font-size: 3rem;
margin-bottom: 14px;
opacity: 0.4;
color: var(--color-accent);
}
.empty-state p {
font-size: 0.95rem;
margin-bottom: 16px;
color: #666;
}
.empty-state .btn-browse {
display: inline-flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #fcb1d8 0%, #f6ccde 100%);
color: var(--color-text-main);
text-decoration: none;
padding: 12px 24px;
border-radius: 10px;
font-size: 0.9rem;
font-weight: 600;
transition: all 0.3s;
}
.empty-state .btn-browse:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(252, 177, 216, 0.4);
}
/* Item List */
.item-list {
display: flex;
flex-direction: column;
gap: 14px;
}
.item {
display: flex;
align-items: center;
gap: 14px;
padding: 14px;
background: #fafafa;
border-radius: 12px;
transition: all 0.2s;
}
.item:hover {
background: #f5f5f5;
}
.item-image {
width: 56px;
height: 56px;
background: linear-gradient(
135deg,
rgba(252, 177, 216, 0.2) 0%,
rgba(246, 204, 222, 0.2) 100%
);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.item-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.item-image i {
font-size: 1.4rem;
color: #e85a9c;
}
.item-details {
flex: 1;
}
.item-name {
color: var(--color-text-main);
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 4px;
}
.item-price {
color: #e85a9c;
font-size: 0.85rem;
font-weight: 600;
}
.item-qty {
color: #888;
font-size: 0.8rem;
}
/* Profile Section */
.profile-card {
grid-column: 1 / -1;
}
.profile-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}
@media (max-width: 600px) {
.profile-grid {
grid-template-columns: 1fr;
}
}
.profile-field {
display: flex;
flex-direction: column;
gap: 6px;
}
.profile-field label {
color: #888;
font-size: 0.85rem;
font-weight: 500;
}
.profile-field .value {
color: var(--color-text-main);
font-size: 1rem;
font-weight: 500;
}
.edit-profile-btn {
margin-top: 24px;
background: linear-gradient(135deg, #fcb1d8 0%, #f6ccde 100%);
color: var(--color-text-main);
border: none;
padding: 14px 28px;
border-radius: 12px;
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 8px;
font-family: inherit;
}
.edit-profile-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(252, 177, 216, 0.4);
}
/* Loading State */
.loading {
display: flex;
align-items: center;
justify-content: center;
min-height: 60vh;
}
.loading-spinner {
width: 48px;
height: 48px;
border: 3px solid rgba(252, 177, 216, 0.3);
border-top-color: var(--color-accent);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Toast */
.toast {
position: fixed;
top: 24px;
right: 24px;
padding: 16px 24px;
border-radius: 12px;
color: white;
font-weight: 500;
font-size: 0.95rem;
display: flex;
align-items: center;
gap: 12px;
transform: translateX(120%);
transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
z-index: 1000;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
.toast.show {
transform: translateX(0);
}
.toast.success {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
}
.toast.error {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
}
.toast.info {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
}
</style>
</head>
<body>
<!-- Toast -->
<div class="toast" id="toast">
<i class="bi bi-check-circle"></i>
<span id="toastMessage">Message</span>
</div>
<!-- Header -->
<header class="header">
<div class="header-content">
<a href="/" class="logo">
<img
src="/uploads/cat-logo-only-1766962993568-201212396.png"
alt="Sky Art Shop"
/>
<span>Sky Art Shop</span>
</a>
<div class="header-actions">
<a href="/" class="nav-link">Shop</a>
<a href="/portfolio" class="nav-link">Portfolio</a>
<button class="logout-btn" onclick="logout()">
<i class="bi bi-box-arrow-right"></i> Sign Out
</button>
</div>
</div>
</header>
<!-- Loading State -->
<div class="loading" id="loadingState">
<div class="loading-spinner"></div>
</div>
<!-- Main Content -->
<main class="main-content" id="mainContent" style="display: none">
<!-- Welcome Section -->
<section class="welcome-section">
<div class="avatar" id="userAvatar">J</div>
<div class="welcome-text">
<h1>Welcome back, <span id="userName">User</span>!</h1>
<p>Manage your account, view your cart, and track your orders</p>
</div>
</section>
<!-- Stats Grid -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon">
<i class="bi bi-cart3"></i>
</div>
<div class="stat-value" id="cartCount">0</div>
<div class="stat-label">Cart Items</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="bi bi-heart"></i>
</div>
<div class="stat-value" id="wishlistCount">0</div>
<div class="stat-label">Wishlist</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="bi bi-box-seam"></i>
</div>
<div class="stat-value" id="ordersCount">0</div>
<div class="stat-label">Orders</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="bi bi-star"></i>
</div>
<div class="stat-value" id="reviewsCount">0</div>
<div class="stat-label">Reviews</div>
</div>
</div>
<!-- Content Grid -->
<div class="content-grid">
<!-- Cart -->
<div class="content-card">
<div class="card-header">
<h3><i class="bi bi-cart3"></i> My Cart</h3>
<a href="/cart" class="view-all">View All</a>
</div>
<div class="card-content" id="cartContent">
<div class="empty-state">
<i class="bi bi-cart3"></i>
<p>Your cart is empty</p>
<a href="/" class="btn-browse">
<i class="bi bi-shop"></i> Browse Shop
</a>
</div>
</div>
</div>
<!-- Wishlist -->
<div class="content-card">
<div class="card-header">
<h3><i class="bi bi-heart"></i> My Wishlist</h3>
<a href="/wishlist" class="view-all">View All</a>
</div>
<div class="card-content" id="wishlistContent">
<div class="empty-state">
<i class="bi bi-heart"></i>
<p>Your wishlist is empty</p>
<a href="/" class="btn-browse">
<i class="bi bi-search"></i> Discover Art
</a>
</div>
</div>
</div>
<!-- Profile -->
<div class="content-card profile-card">
<div class="card-header">
<h3><i class="bi bi-person"></i> Profile Information</h3>
</div>
<div class="card-content">
<div class="profile-grid">
<div class="profile-field">
<label>First Name</label>
<div class="value" id="profileFirstName">-</div>
</div>
<div class="profile-field">
<label>Last Name</label>
<div class="value" id="profileLastName">-</div>
</div>
<div class="profile-field">
<label>Email Address</label>
<div class="value" id="profileEmail">-</div>
</div>
<div class="profile-field">
<label>Member Since</label>
<div class="value" id="profileSince">-</div>
</div>
</div>
<button
class="edit-profile-btn"
onclick="showToast('Profile editing coming soon!', 'info')"
>
<i class="bi bi-pencil"></i> Edit Profile
</button>
</div>
</div>
</div>
</main>
<script>
let customer = null;
function showToast(message, type = "success") {
const toast = document.getElementById("toast");
const toastMessage = document.getElementById("toastMessage");
const icon = toast.querySelector("i");
toastMessage.textContent = message;
toast.className = "toast " + type;
icon.className = "bi";
if (type === "success") icon.classList.add("bi-check-circle");
else if (type === "error") icon.classList.add("bi-exclamation-circle");
else if (type === "info") icon.classList.add("bi-info-circle");
toast.classList.add("show");
setTimeout(() => toast.classList.remove("show"), 4000);
}
function logout() {
localStorage.removeItem("customer");
fetch("/api/customers/logout", {
method: "POST",
credentials: "include",
}).finally(() => {
window.location.href = "/signin";
});
}
async function loadAccountData() {
const stored = localStorage.getItem("customer");
if (!stored) {
window.location.href = "/signin";
return;
}
try {
customer = JSON.parse(stored);
} catch (e) {
window.location.href = "/signin";
return;
}
document.getElementById("userName").textContent =
customer.firstName || "User";
document.getElementById("userAvatar").textContent = (
customer.firstName || "U"
)
.charAt(0)
.toUpperCase();
document.getElementById("profileFirstName").textContent =
customer.firstName || "-";
document.getElementById("profileLastName").textContent =
customer.lastName || "-";
document.getElementById("profileEmail").textContent =
customer.email || "-";
if (customer.createdAt) {
const date = new Date(customer.createdAt);
document.getElementById("profileSince").textContent =
date.toLocaleDateString("en-US", {
month: "long",
year: "numeric",
});
}
try {
const [cartRes, wishlistRes] = await Promise.all([
fetch("/api/customers/cart/count", { credentials: "include" }),
fetch("/api/customers/wishlist/count", { credentials: "include" }),
]);
if (cartRes.ok) {
const cartData = await cartRes.json();
document.getElementById("cartCount").textContent =
cartData.count || 0;
}
if (wishlistRes.ok) {
const wishlistData = await wishlistRes.json();
document.getElementById("wishlistCount").textContent =
wishlistData.count || 0;
}
} catch (e) {
console.error("Error loading counts:", e);
}
try {
const cartItemsRes = await fetch("/api/customers/cart", {
credentials: "include",
});
if (cartItemsRes.ok) {
const cartItems = await cartItemsRes.json();
if (cartItems.items && cartItems.items.length > 0) {
renderCartItems(cartItems.items.slice(0, 3));
}
}
} catch (e) {
console.error("Error loading cart items:", e);
}
try {
const wishlistRes = await fetch("/api/customers/wishlist", {
credentials: "include",
});
if (wishlistRes.ok) {
const wishlistItems = await wishlistRes.json();
if (wishlistItems.items && wishlistItems.items.length > 0) {
renderWishlistItems(wishlistItems.items.slice(0, 3));
}
}
} catch (e) {
console.error("Error loading wishlist:", e);
}
document.getElementById("loadingState").style.display = "none";
document.getElementById("mainContent").style.display = "block";
}
function renderCartItems(items) {
const container = document.getElementById("cartContent");
if (!items || items.length === 0) return;
container.innerHTML = `
<div class="item-list">
${items
.map(
(item) => `
<div class="item">
<div class="item-image">
${
item.product_image
? `<img src="${item.product_image}" alt="${item.product_name}">`
: `<i class="bi bi-image"></i>`
}
</div>
<div class="item-details">
<div class="item-name">${item.product_name || "Product"}</div>
<div class="item-price">$${parseFloat(item.price || 0).toFixed(
2
)}</div>
<div class="item-qty">Qty: ${item.quantity || 1}</div>
</div>
</div>
`
)
.join("")}
</div>
`;
}
function renderWishlistItems(items) {
const container = document.getElementById("wishlistContent");
if (!items || items.length === 0) return;
container.innerHTML = `
<div class="item-list">
${items
.map(
(item) => `
<div class="item">
<div class="item-image">
${
item.product_image
? `<img src="${item.product_image}" alt="${item.product_name}">`
: `<i class="bi bi-image"></i>`
}
</div>
<div class="item-details">
<div class="item-name">${item.product_name || "Product"}</div>
<div class="item-price">$${parseFloat(item.price || 0).toFixed(
2
)}</div>
</div>
</div>
`
)
.join("")}
</div>
`;
}
document.addEventListener("DOMContentLoaded", loadAccountData);
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
<!-- Gradient definitions -->
<defs>
<linearGradient id="catGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#FF6B9D;stop-opacity:1" />
<stop offset="100%" style="stop-color:#C239B3;stop-opacity:1" />
</linearGradient>
<linearGradient id="accentGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#FEC6DF;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FF6B9D;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background circle -->
<circle cx="100" cy="100" r="95" fill="url(#accentGradient)" opacity="0.2"/>
<!-- Cat body -->
<ellipse cx="100" cy="130" rx="45" ry="50" fill="url(#catGradient)"/>
<!-- Cat head -->
<circle cx="100" cy="80" r="35" fill="url(#catGradient)"/>
<!-- Left ear -->
<path d="M 75 55 L 65 30 L 85 50 Z" fill="url(#catGradient)"/>
<path d="M 75 55 L 70 35 L 82 52 Z" fill="#FEC6DF"/>
<!-- Right ear -->
<path d="M 125 55 L 135 30 L 115 50 Z" fill="url(#catGradient)"/>
<path d="M 125 55 L 130 35 L 118 52 Z" fill="#FEC6DF"/>
<!-- Left eye -->
<ellipse cx="88" cy="75" rx="6" ry="10" fill="#2D3436"/>
<ellipse cx="89" cy="73" rx="2" ry="3" fill="white"/>
<!-- Right eye -->
<ellipse cx="112" cy="75" rx="6" ry="10" fill="#2D3436"/>
<ellipse cx="113" cy="73" rx="2" ry="3" fill="white"/>
<!-- Nose -->
<path d="M 100 85 L 97 90 L 103 90 Z" fill="#FF6B9D"/>
<!-- Mouth -->
<path d="M 100 90 Q 95 93 92 91" stroke="#2D3436" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<path d="M 100 90 Q 105 93 108 91" stroke="#2D3436" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<!-- Whiskers left -->
<line x1="70" y1="80" x2="50" y2="78" stroke="#2D3436" stroke-width="1.5" stroke-linecap="round"/>
<line x1="70" y1="85" x2="50" y2="85" stroke="#2D3436" stroke-width="1.5" stroke-linecap="round"/>
<line x1="70" y1="90" x2="50" y2="92" stroke="#2D3436" stroke-width="1.5" stroke-linecap="round"/>
<!-- Whiskers right -->
<line x1="130" y1="80" x2="150" y2="78" stroke="#2D3436" stroke-width="1.5" stroke-linecap="round"/>
<line x1="130" y1="85" x2="150" y2="85" stroke="#2D3436" stroke-width="1.5" stroke-linecap="round"/>
<line x1="130" y1="90" x2="150" y2="92" stroke="#2D3436" stroke-width="1.5" stroke-linecap="round"/>
<!-- Paws -->
<ellipse cx="80" cy="170" rx="12" ry="8" fill="#FF6B9D"/>
<ellipse cx="120" cy="170" rx="12" ry="8" fill="#FF6B9D"/>
<!-- Tail -->
<path d="M 140 140 Q 160 130 165 110 Q 168 90 160 75" stroke="url(#catGradient)" stroke-width="12" fill="none" stroke-linecap="round"/>
<!-- Heart on chest (art/creativity symbol) -->
<path d="M 100 120 L 95 115 Q 93 112 93 109 Q 93 106 95 104 Q 97 102 100 104 Q 103 102 105 104 Q 107 106 107 109 Q 107 112 105 115 Z" fill="#FEC6DF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,616 @@
/**
* Accessibility Enhancements
* Adds ARIA labels, focus states, keyboard navigation, and screen reader support
*/
(function () {
"use strict";
console.log("[Accessibility] Loading...");
const A11y = {
init() {
this.addARIALabels();
this.enhanceKeyboardNavigation();
this.addSkipLinks();
this.enhanceFocusStates();
this.announceLiveRegions();
this.fixMobileAccessibility();
console.log("[Accessibility] Initialized");
},
// Add ARIA labels to interactive elements
addARIALabels() {
// Navbar
const navbar = document.querySelector(".navbar");
if (navbar) {
navbar.setAttribute("role", "navigation");
navbar.setAttribute("aria-label", "Main navigation");
}
// Nav menu
const navMenu = document.querySelector(".nav-menu");
if (navMenu) {
navMenu.setAttribute("role", "menubar");
navMenu.setAttribute("aria-label", "Primary menu");
navMenu.querySelectorAll(".nav-link").forEach((link, index) => {
link.setAttribute("role", "menuitem");
link.setAttribute("tabindex", "0");
});
}
// Nav actions buttons
const navActions = document.querySelector(".nav-actions");
if (navActions) {
navActions.setAttribute("role", "group");
navActions.setAttribute("aria-label", "Account and cart actions");
}
// Cart button
const cartBtn = document.querySelector(".cart-btn");
if (cartBtn) {
cartBtn.setAttribute("aria-label", "Shopping cart");
cartBtn.setAttribute("aria-haspopup", "dialog");
const cartCount = cartBtn.querySelector(".cart-count");
if (cartCount) {
cartBtn.setAttribute("aria-describedby", "cart-count-desc");
// Create hidden description for screen readers
if (!document.getElementById("cart-count-desc")) {
const desc = document.createElement("span");
desc.id = "cart-count-desc";
desc.className = "sr-only";
desc.textContent = "items in cart";
cartBtn.appendChild(desc);
}
}
}
// Wishlist button
const wishlistBtn = document.querySelector(".wishlist-btn-nav");
if (wishlistBtn) {
wishlistBtn.setAttribute("aria-label", "Wishlist");
wishlistBtn.setAttribute("aria-haspopup", "dialog");
}
// Sign in link
const signinLink = document.querySelector('a[href="/signin"]');
if (signinLink) {
signinLink.setAttribute("aria-label", "Sign in to your account");
}
// Mobile menu toggle
const mobileToggle = document.querySelector(".nav-mobile-toggle");
if (mobileToggle) {
mobileToggle.setAttribute("aria-expanded", "false");
mobileToggle.setAttribute("aria-controls", "nav-menu");
mobileToggle.setAttribute("aria-label", "Toggle navigation menu");
}
// Product cards
document.querySelectorAll(".product-card").forEach((card, index) => {
card.setAttribute("role", "article");
const title = card.querySelector(".product-title, .product-name, h3");
if (title) {
card.setAttribute("aria-label", title.textContent.trim());
}
// Quick view button
const quickView = card.querySelector(
'.quick-view-btn, [data-action="quick-view"]'
);
if (quickView) {
quickView.setAttribute(
"aria-label",
`Quick view ${title ? title.textContent.trim() : "product"}`
);
}
// Add to cart button
const addCart = card.querySelector(
'.add-to-cart-btn, [data-action="add-to-cart"]'
);
if (addCart) {
addCart.setAttribute(
"aria-label",
`Add ${title ? title.textContent.trim() : "product"} to cart`
);
}
// Wishlist button
const wishlist = card.querySelector(
'.wishlist-btn, [data-action="wishlist"]'
);
if (wishlist) {
wishlist.setAttribute(
"aria-label",
`Add ${title ? title.textContent.trim() : "product"} to wishlist`
);
wishlist.setAttribute("aria-pressed", "false");
}
});
// Slider controls
const sliderPrev = document.querySelector(".slider-arrow.prev");
const sliderNext = document.querySelector(".slider-arrow.next");
if (sliderPrev) sliderPrev.setAttribute("aria-label", "Previous slide");
if (sliderNext) sliderNext.setAttribute("aria-label", "Next slide");
// Slider nav dots
document
.querySelectorAll(".slider-nav .dot, .slider-dot")
.forEach((dot, index) => {
dot.setAttribute("role", "tab");
dot.setAttribute("aria-label", `Go to slide ${index + 1}`);
dot.setAttribute(
"aria-selected",
dot.classList.contains("active") ? "true" : "false"
);
});
// Cart drawer
const cartDrawer = document.querySelector(".cart-drawer");
if (cartDrawer) {
cartDrawer.setAttribute("role", "dialog");
cartDrawer.setAttribute("aria-modal", "true");
cartDrawer.setAttribute("aria-label", "Shopping cart");
const closeBtn = cartDrawer.querySelector(".cart-close, .close-cart");
if (closeBtn) {
closeBtn.setAttribute("aria-label", "Close cart");
}
}
// Wishlist drawer
const wishlistDrawer = document.querySelector(".wishlist-drawer");
if (wishlistDrawer) {
wishlistDrawer.setAttribute("role", "dialog");
wishlistDrawer.setAttribute("aria-modal", "true");
wishlistDrawer.setAttribute("aria-label", "Wishlist");
const closeBtn = wishlistDrawer.querySelector(
".wishlist-close, .close-wishlist"
);
if (closeBtn) {
closeBtn.setAttribute("aria-label", "Close wishlist");
}
}
// Form inputs
document
.querySelectorAll("input:not([aria-label]):not([aria-labelledby])")
.forEach((input) => {
const label =
input.closest("label") ||
document.querySelector(`label[for="${input.id}"]`);
if (label) {
if (!input.id) {
input.id = `input-${Math.random().toString(36).substr(2, 9)}`;
label.setAttribute("for", input.id);
}
} else {
const placeholder = input.getAttribute("placeholder");
if (placeholder) {
input.setAttribute("aria-label", placeholder);
}
}
});
// Images without alt text
document.querySelectorAll("img:not([alt])").forEach((img) => {
img.setAttribute("alt", "");
img.setAttribute("role", "presentation");
});
// Footer
const footer = document.querySelector("footer, .footer");
if (footer) {
footer.setAttribute("role", "contentinfo");
}
// Main content
const main = document.querySelector("main, .page-content");
if (main) {
main.setAttribute("role", "main");
main.id = main.id || "main-content";
}
// Sections
document.querySelectorAll("section").forEach((section) => {
const heading = section.querySelector("h1, h2, h3");
if (heading) {
section.setAttribute(
"aria-labelledby",
heading.id ||
(heading.id = `heading-${Math.random()
.toString(36)
.substr(2, 9)}`)
);
}
});
},
// Enhance keyboard navigation
enhanceKeyboardNavigation() {
// ESC key to close modals/drawers
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
// Close cart drawer
const cartDrawer = document.querySelector(
".cart-drawer.open, .cart-drawer.active"
);
if (cartDrawer) {
const closeBtn = cartDrawer.querySelector(
".cart-close, .close-cart"
);
if (closeBtn) closeBtn.click();
document.querySelector(".cart-btn")?.focus();
}
// Close wishlist drawer
const wishlistDrawer = document.querySelector(
".wishlist-drawer.open, .wishlist-drawer.active"
);
if (wishlistDrawer) {
const closeBtn = wishlistDrawer.querySelector(
".wishlist-close, .close-wishlist"
);
if (closeBtn) closeBtn.click();
document.querySelector(".wishlist-btn-nav")?.focus();
}
// Close mobile menu
const navMenu = document.querySelector(
".nav-menu.open, .nav-menu.active"
);
if (navMenu) {
document.querySelector(".nav-mobile-toggle")?.click();
}
// Close modals
const modal = document.querySelector(".modal.show, .modal.open");
if (modal) {
const closeBtn = modal.querySelector(
'.modal-close, .close-modal, [data-dismiss="modal"]'
);
if (closeBtn) closeBtn.click();
}
// Close user dropdown
const userDropdown = document.querySelector(
".user-dropdown-menu.show"
);
if (userDropdown) {
userDropdown.classList.remove("show");
}
}
});
// Arrow key navigation for nav menu
const navLinks = document.querySelectorAll(".nav-menu .nav-link");
navLinks.forEach((link, index) => {
link.addEventListener("keydown", (e) => {
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
e.preventDefault();
const next = navLinks[index + 1] || navLinks[0];
next.focus();
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
e.preventDefault();
const prev = navLinks[index - 1] || navLinks[navLinks.length - 1];
prev.focus();
}
});
});
// Enter/Space for clickable elements
document
.querySelectorAll('[role="button"], [role="tab"], .product-card')
.forEach((el) => {
if (!el.getAttribute("tabindex")) {
el.setAttribute("tabindex", "0");
}
el.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
el.click();
}
});
});
// Focus trap for modals/drawers
this.setupFocusTrap(".cart-drawer");
this.setupFocusTrap(".wishlist-drawer");
this.setupFocusTrap(".modal");
},
// Setup focus trap for modal-like elements
setupFocusTrap(selector) {
const container = document.querySelector(selector);
if (!container) return;
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === "class") {
const isOpen =
container.classList.contains("open") ||
container.classList.contains("active") ||
container.classList.contains("show");
if (isOpen) {
this.trapFocus(container);
}
}
});
});
observer.observe(container, { attributes: true });
},
// Trap focus within container
trapFocus(container) {
const focusableElements = container.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length === 0) return;
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
firstElement.focus();
container.addEventListener("keydown", function trapHandler(e) {
if (e.key !== "Tab") return;
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
});
},
// Add skip to main content link
addSkipLinks() {
if (document.querySelector(".skip-link")) return;
const main = document.querySelector("main, .page-content, #main-content");
if (!main) return;
main.id = main.id || "main-content";
const skipLink = document.createElement("a");
skipLink.href = "#" + main.id;
skipLink.className = "skip-link";
skipLink.textContent = "Skip to main content";
skipLink.setAttribute("tabindex", "0");
document.body.insertBefore(skipLink, document.body.firstChild);
// Add CSS for skip link
if (!document.getElementById("skip-link-styles")) {
const style = document.createElement("style");
style.id = "skip-link-styles";
style.textContent = `
.skip-link {
position: absolute;
top: -100px;
left: 50%;
transform: translateX(-50%);
background: var(--primary-pink, #f8c8dc);
color: var(--text-primary, #333);
padding: 12px 24px;
border-radius: 0 0 8px 8px;
font-weight: 600;
text-decoration: none;
z-index: 10000;
transition: top 0.3s ease;
}
.skip-link:focus {
top: 0;
outline: 3px solid var(--accent-pink, #ff69b4);
outline-offset: 2px;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
`;
document.head.appendChild(style);
}
},
// Enhance focus states for better visibility
enhanceFocusStates() {
if (document.getElementById("focus-state-styles")) return;
const style = document.createElement("style");
style.id = "focus-state-styles";
style.textContent = `
/* Enhanced focus states for accessibility */
:focus {
outline: 2px solid var(--accent-pink, #ff69b4);
outline-offset: 2px;
}
:focus:not(:focus-visible) {
outline: none;
}
:focus-visible {
outline: 3px solid var(--accent-pink, #ff69b4);
outline-offset: 2px;
}
/* Button focus */
.btn:focus-visible,
button:focus-visible {
outline: 3px solid var(--accent-pink, #ff69b4);
outline-offset: 2px;
box-shadow: 0 0 0 4px rgba(255, 105, 180, 0.25);
}
/* Link focus */
a:focus-visible {
outline: 2px solid var(--accent-pink, #ff69b4);
outline-offset: 2px;
border-radius: 2px;
}
/* Input focus */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: 2px solid var(--primary-pink, #f8c8dc);
outline-offset: 0;
border-color: var(--accent-pink, #ff69b4);
box-shadow: 0 0 0 3px rgba(248, 200, 220, 0.3);
}
/* Card focus */
.product-card:focus-visible,
.blog-card:focus-visible {
outline: 3px solid var(--accent-pink, #ff69b4);
outline-offset: 4px;
}
/* Nav link focus */
.nav-link:focus-visible {
background: rgba(248, 200, 220, 0.3);
border-radius: var(--radius-sm, 4px);
}
/* Icon button focus */
.nav-icon-btn:focus-visible {
outline: 2px solid var(--accent-pink, #ff69b4);
outline-offset: 2px;
background: rgba(248, 200, 220, 0.3);
}
`;
document.head.appendChild(style);
},
// Setup live regions for dynamic content announcements
announceLiveRegions() {
// Create live region for announcements
if (!document.getElementById("a11y-live-region")) {
const liveRegion = document.createElement("div");
liveRegion.id = "a11y-live-region";
liveRegion.setAttribute("aria-live", "polite");
liveRegion.setAttribute("aria-atomic", "true");
liveRegion.className = "sr-only";
document.body.appendChild(liveRegion);
}
// Announce cart updates
const originalAddToCart = window.ShopState?.addToCart;
if (originalAddToCart) {
window.ShopState.addToCart = function (...args) {
const result = originalAddToCart.apply(this, args);
A11y.announce("Item added to cart");
return result;
};
}
// Announce wishlist updates
const originalAddToWishlist = window.ShopState?.addToWishlist;
if (originalAddToWishlist) {
window.ShopState.addToWishlist = function (...args) {
const result = originalAddToWishlist.apply(this, args);
A11y.announce("Item added to wishlist");
return result;
};
}
},
// Announce message to screen readers
announce(message) {
const liveRegion = document.getElementById("a11y-live-region");
if (liveRegion) {
liveRegion.textContent = message;
setTimeout(() => {
liveRegion.textContent = "";
}, 1000);
}
},
// Mobile accessibility enhancements
fixMobileAccessibility() {
// Ensure touch targets are at least 44x44px
const style = document.createElement("style");
style.id = "mobile-a11y-styles";
style.textContent = `
@media (max-width: 768px) {
/* Minimum touch target size */
button,
.btn,
.nav-icon-btn,
.nav-link,
input[type="button"],
input[type="submit"],
a {
min-height: 44px;
min-width: 44px;
}
/* Ensure adequate spacing for touch */
.nav-actions {
gap: 8px;
}
/* Larger tap targets for mobile menu */
.nav-menu .nav-link {
padding: 16px 20px;
}
}
`;
if (!document.getElementById("mobile-a11y-styles")) {
document.head.appendChild(style);
}
// Update mobile toggle aria-expanded on click
const mobileToggle = document.querySelector(".nav-mobile-toggle");
if (mobileToggle) {
const observer = new MutationObserver(() => {
const navMenu = document.querySelector(".nav-menu");
const isOpen =
navMenu?.classList.contains("open") ||
navMenu?.classList.contains("active") ||
document.body.classList.contains("nav-open");
mobileToggle.setAttribute("aria-expanded", isOpen ? "true" : "false");
});
observer.observe(document.body, {
attributes: true,
subtree: true,
attributeFilter: ["class"],
});
}
},
};
// Initialize on DOM ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => A11y.init());
} else {
A11y.init();
}
// Expose for external use
window.A11y = A11y;
})();

View File

@@ -0,0 +1,421 @@
/**
* Shopping Cart Component
* Handles cart dropdown, updates, and interactions
*/
(function () {
"use strict";
// Base Dropdown Component
class BaseDropdown {
constructor(config) {
this.toggleBtn = document.getElementById(config.toggleId);
this.panel = document.getElementById(config.panelId);
this.content = document.getElementById(config.contentId);
this.closeBtn = document.getElementById(config.closeId);
this.wrapperClass = config.wrapperClass;
this.eventName = config.eventName;
this.emptyMessage = config.emptyMessage;
this.isOpen = false;
this.init();
}
init() {
this.setupEventListeners();
this.render();
}
setupEventListeners() {
if (this.toggleBtn) {
this.toggleBtn.addEventListener("click", () => this.toggle());
}
if (this.closeBtn) {
this.closeBtn.addEventListener("click", () => this.close());
}
document.addEventListener("click", (e) => {
if (this.isOpen && !e.target.closest(this.wrapperClass)) {
this.close();
}
});
window.addEventListener(this.eventName, () => {
console.log(`[${this.constructor.name}] ${this.eventName} received`);
this.render();
});
}
toggle() {
this.isOpen ? this.close() : this.open();
}
open() {
if (this.panel) {
this.panel.classList.add("active");
this.panel.setAttribute("aria-hidden", "false");
this.isOpen = true;
this.render();
}
}
close() {
if (this.panel) {
this.panel.classList.remove("active");
this.panel.setAttribute("aria-hidden", "true");
this.isOpen = false;
}
}
renderEmpty() {
if (this.content) {
this.content.innerHTML = this.emptyMessage;
}
}
}
class ShoppingCart extends BaseDropdown {
constructor() {
super({
toggleId: "cartToggle",
panelId: "cartPanel",
contentId: "cartContent",
closeId: "cartClose",
wrapperClass: ".cart-dropdown-wrapper",
eventName: "cart-updated",
emptyMessage:
'<p class="empty-state"><i class="bi bi-cart-x"></i><br>Your cart is empty</p>',
});
}
render() {
if (!this.content) return;
try {
if (!window.AppState) {
return;
}
const cart = window.AppState.cart;
if (!Array.isArray(cart)) {
this.content.innerHTML =
'<p class="empty-state">Error loading cart</p>';
return;
}
if (cart.length === 0) {
this.renderEmpty();
this.updateFooter(null);
return;
}
const validItems = this._filterValidItems(cart);
if (validItems.length === 0) {
this.renderEmpty();
this.updateFooter(null);
return;
}
this.content.innerHTML = validItems
.map((item) => this.renderCartItem(item))
.join("");
this.setupCartItemListeners();
const total = this._calculateTotal(validItems);
this.updateFooter(total);
} catch (error) {
this.content.innerHTML =
'<p class="empty-state">Error loading cart</p>';
}
}
_filterValidItems(items) {
return items.filter(
(item) => item && item.id && typeof item.price !== "undefined"
);
}
_calculateTotal(items) {
if (window.AppState.getCartTotal) {
return window.AppState.getCartTotal();
}
return items.reduce((sum, item) => {
const price = parseFloat(item.price) || 0;
const quantity = parseInt(item.quantity) || 0;
return sum + price * quantity;
}, 0);
}
renderCartItem(item) {
try {
// Validate item and Utils availability
if (!item || !item.id) {
return "";
}
if (!window.Utils) {
return '<p class="error-message">Error loading item</p>';
}
// Sanitize and validate item data with defensive checks
const imageUrl =
item.image ||
item.imageurl ||
item.imageUrl ||
item.image_url ||
"/assets/images/placeholder.svg";
const title = window.Utils.escapeHtml(
item.title || item.name || "Product"
);
const color = item.color ? window.Utils.escapeHtml(item.color) : null;
const price = parseFloat(item.price) || 0;
const quantity = Math.max(1, parseInt(item.quantity) || 1);
const subtotal = price * quantity;
const priceFormatted = window.Utils.formatCurrency(price);
const subtotalFormatted = window.Utils.formatCurrency(subtotal);
return `
<div class="cart-item" data-id="${item.id}">
<img src="${imageUrl}" alt="${title}" class="cart-item-image" loading="lazy" onerror="this.src='/assets/images/placeholder.svg'">
<div class="cart-item-details">
<h4 class="cart-item-title">${title}</h4>
${
color
? `<p class="cart-item-color" style="font-size: 0.85rem; color: #666; margin: 2px 0;">Color: ${color}</p>`
: ""
}
<p class="cart-item-price">${priceFormatted}</p>
<div class="cart-item-quantity">
<button class="quantity-btn quantity-minus" data-id="${
item.id
}" aria-label="Decrease quantity">
<i class="bi bi-dash"></i>
</button>
<span class="quantity-value">${quantity}</span>
<button class="quantity-btn quantity-plus" data-id="${
item.id
}" aria-label="Increase quantity">
<i class="bi bi-plus"></i>
</button>
</div>
<p class="cart-item-subtotal">Subtotal: ${subtotalFormatted}</p>
</div>
<button class="cart-item-remove" data-id="${
item.id
}" aria-label="Remove from cart">
<i class="bi bi-x-lg"></i>
</button>
</div>
`;
} catch (error) {
return "";
}
}
setupCartItemListeners() {
try {
this._setupRemoveButtons();
this._setupQuantityButtons();
} catch (error) {
console.error("[ShoppingCart] Error setting up listeners:", error);
}
}
_setupRemoveButtons() {
this.content.querySelectorAll(".cart-item-remove").forEach((btn) => {
btn.addEventListener("click", (e) => {
e.stopPropagation();
this._handleAction(e, () => {
const id = e.currentTarget.dataset.id;
if (id && window.AppState?.removeFromCart) {
window.AppState.removeFromCart(id);
this.render();
}
});
});
});
}
_setupQuantityButtons() {
this._setupQuantityButton(".quantity-minus", -1);
this._setupQuantityButton(".quantity-plus", 1);
}
_setupQuantityButton(selector, delta) {
this.content.querySelectorAll(selector).forEach((btn) => {
btn.addEventListener("click", (e) => {
e.stopPropagation();
this._handleAction(e, () => {
const id = e.currentTarget.dataset.id;
if (!window.AppState?.cart) return;
const item = window.AppState.cart.find(
(item) => String(item.id) === String(id)
);
if (!item || !window.AppState.updateCartQuantity) return;
const newQuantity =
delta > 0
? Math.min(item.quantity + delta, 999)
: Math.max(item.quantity + delta, 1);
if (delta < 0 && item.quantity <= 1) return;
window.AppState.updateCartQuantity(id, newQuantity);
this.render();
});
});
});
}
_handleAction(event, callback) {
try {
callback();
} catch (error) {
console.error("[ShoppingCart] Action error:", error);
}
}
updateFooter(total) {
const footer = this.cartPanel?.querySelector(".dropdown-foot");
if (!footer) return;
if (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>
`;
}
}
}
// Wishlist Component
class Wishlist extends BaseDropdown {
constructor() {
super({
toggleId: "wishlistToggle",
panelId: "wishlistPanel",
contentId: "wishlistContent",
closeId: "wishlistClose",
wrapperClass: ".wishlist-dropdown-wrapper",
eventName: "wishlist-updated",
emptyMessage:
'<p class="empty-state"><i class="bi bi-heart"></i><br>Your wishlist is empty</p>',
});
}
render() {
if (!this.content) return;
if (!window.AppState) {
console.warn("[Wishlist] AppState not available yet");
return;
}
const wishlist = window.AppState.wishlist;
if (wishlist.length === 0) {
this.renderEmpty();
return;
}
this.content.innerHTML = wishlist
.map((item) => this.renderWishlistItem(item))
.join("");
this.setupWishlistItemListeners();
}
renderWishlistItem(item) {
if (!window.Utils) {
console.error("[Wishlist] Utils not available");
return '<p class="error-message">Error loading item</p>';
}
const imageUrl =
item.imageurl ||
item.imageUrl ||
item.image_url ||
"/assets/images/placeholder.jpg";
const title = window.Utils.escapeHtml(
item.title || item.name || "Product"
);
const price = window.Utils.formatCurrency(parseFloat(item.price) || 0);
return `
<div class="wishlist-item" data-id="${item.id}">
<img src="${imageUrl}" alt="${title}" class="wishlist-item-image" loading="lazy" onerror="this.src='/assets/images/placeholder.svg'">
<div class="wishlist-item-details">
<h4 class="wishlist-item-title">${title}</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}" aria-label="Remove from wishlist">
<i class="bi bi-x-lg"></i>
</button>
</div>
`;
}
setupWishlistItemListeners() {
this._setupRemoveButtons();
this._setupAddToCartButtons();
}
_setupRemoveButtons() {
this.content.querySelectorAll(".wishlist-item-remove").forEach((btn) => {
btn.addEventListener("click", (e) => {
e.stopPropagation();
const id = e.currentTarget.dataset.id;
if (window.AppState?.removeFromWishlist) {
window.AppState.removeFromWishlist(id);
this.render();
}
});
});
}
_setupAddToCartButtons() {
this.content.querySelectorAll(".btn-add-to-cart").forEach((btn) => {
btn.addEventListener("click", (e) => {
e.stopPropagation();
const id = e.currentTarget.dataset.id;
const item = window.AppState?.wishlist.find(
(item) => String(item.id) === String(id)
);
if (item && window.AppState?.addToCart) {
window.AppState.addToCart(item);
}
});
});
}
}
// Initialize when DOM is ready
const initializeComponents = () => {
// Skip if shop-system.js already initialized
if (window.ShopSystem?.isInitialized) {
console.log(
"[cart.js] Skipping initialization - shop-system.js already loaded"
);
return;
}
console.log("[cart.js] Initializing ShoppingCart and Wishlist components");
new ShoppingCart();
new Wishlist();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initializeComponents);
} else {
initializeComponents();
}
})();

View File

@@ -0,0 +1,540 @@
/**
* Shared HTML Components
* Eliminates duplication across HTML files
*/
// Component templates
const Components = {
/**
* Render cart drawer HTML
* @returns {string} Cart drawer HTML
*/
cartDrawer: () => `
<div id="cartDrawer" class="cart-drawer">
<div class="cart-drawer-header">
<h3>Shopping Cart</h3>
<button id="closeCart" class="close-cart" aria-label="Close cart">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<div id="cartItems" class="cart-items"></div>
<div class="cart-footer">
<div class="cart-total">
<span>Total:</span>
<span id="cartTotal">$0.00</span>
</div>
<button id="checkoutBtn" class="btn btn-primary checkout-btn">
Proceed to Checkout
</button>
</div>
</div>
`,
/**
* Render wishlist drawer HTML
* @returns {string} Wishlist drawer HTML
*/
wishlistDrawer: () => `
<div id="wishlistDrawer" class="wishlist-drawer">
<div class="wishlist-drawer-header">
<h3>Wishlist</h3>
<button id="closeWishlist" class="close-wishlist" aria-label="Close wishlist">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<div id="wishlistItems" class="wishlist-items"></div>
</div>
`,
/**
* Render navbar HTML
* @param {Object} options - Navbar options
* @param {string} options.activePage - Current active page
* @returns {string} Navbar HTML
*/
navbar: ({ activePage = "" } = {}) => {
const isActive = (page) => (page === activePage ? "active" : "");
return `
<nav class="navbar">
<div class="nav-container">
<a href="/" class="nav-logo">
<img src="/assets/images/logo.png" alt="SkyArtShop Logo" class="logo-img" />
<span>SkyArtShop</span>
</a>
<button class="mobile-menu-toggle" id="mobileMenuToggle" aria-label="Toggle menu">
<span></span>
<span></span>
<span></span>
</button>
<div class="nav-menu" id="navMenu">
<a href="/" class="nav-link ${isActive("home")}">Home</a>
<a href="/shop" class="nav-link ${isActive("shop")}">Shop</a>
<a href="/portfolio" class="nav-link ${isActive(
"portfolio"
)}">Portfolio</a>
<a href="/blog" class="nav-link ${isActive("blog")}">Blog</a>
<a href="/about" class="nav-link ${isActive("about")}">About</a>
<a href="/contact" class="nav-link ${isActive(
"contact"
)}">Contact</a>
<div class="nav-actions">
<button id="wishlistBtn" class="icon-btn" aria-label="Wishlist">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
</svg>
<span id="wishlistBadge" class="badge">0</span>
</button>
<button id="cartBtn" class="icon-btn" aria-label="Shopping cart">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="9" cy="21" r="1"></circle>
<circle cx="20" cy="21" r="1"></circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
</svg>
<span id="cartBadge" class="badge">0</span>
</button>
</div>
</div>
</div>
</nav>
`;
},
/**
* Render footer HTML
* @returns {string} Footer HTML
*/
footer: () => `
<footer class="footer">
<div class="footer-container">
<div class="footer-section">
<h3>About SkyArtShop</h3>
<p>Your premier destination for unique art pieces and custom designs.</p>
</div>
<div class="footer-section">
<h3>Quick Links</h3>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/shop">Shop</a></li>
<li><a href="/portfolio">Portfolio</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<div class="footer-section">
<h3>Customer Service</h3>
<ul>
<li><a href="/faq">FAQ</a></li>
<li><a href="/shipping-info">Shipping Info</a></li>
<li><a href="/returns">Returns</a></li>
<li><a href="/privacy">Privacy Policy</a></li>
</ul>
</div>
<div class="footer-section">
<h3>Connect With Us</h3>
<div class="social-links">
<a href="#" aria-label="Facebook">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path>
</svg>
</a>
<a href="#" aria-label="Instagram">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
</svg>
</a>
<a href="#" aria-label="Twitter">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path>
</svg>
</a>
</div>
</div>
</div>
<div class="footer-bottom">
<p>&copy; ${new Date().getFullYear()} SkyArtShop. All rights reserved.</p>
</div>
</footer>
`,
/**
* Render notification container
* @returns {string} Notification container HTML
*/
notificationContainer: () => `
<div id="notificationContainer" class="notification-container"></div>
`,
};
/**
* Initialize components in the DOM
* @param {Object} options - Component options
* @param {boolean} options.navbar - Include navbar
* @param {boolean} options.footer - Include footer
* @param {boolean} options.cart - Include cart drawer
* @param {boolean} options.wishlist - Include wishlist drawer
* @param {boolean} options.notifications - Include notifications
* @param {string} options.activePage - Active page for navbar
*/
function initializeComponents({
navbar = true,
footer = true,
cart = true,
wishlist = true,
notifications = true,
activePage = "",
} = {}) {
// Inject components into DOM
if (navbar) {
const navPlaceholder = document.getElementById("navbar-placeholder");
if (navPlaceholder) {
navPlaceholder.innerHTML = Components.navbar({ activePage });
}
}
if (footer) {
const footerPlaceholder = document.getElementById("footer-placeholder");
if (footerPlaceholder) {
footerPlaceholder.innerHTML = Components.footer();
}
}
if (cart) {
const cartPlaceholder = document.getElementById("cart-drawer-placeholder");
if (cartPlaceholder) {
cartPlaceholder.innerHTML = Components.cartDrawer();
}
}
if (wishlist) {
const wishlistPlaceholder = document.getElementById(
"wishlist-drawer-placeholder"
);
if (wishlistPlaceholder) {
wishlistPlaceholder.innerHTML = Components.wishlistDrawer();
}
}
if (notifications) {
const notificationPlaceholder = document.getElementById(
"notification-placeholder"
);
if (notificationPlaceholder) {
notificationPlaceholder.innerHTML = Components.notificationContainer();
}
}
// Initialize mobile menu toggle
initMobileMenu();
// Initialize cart and wishlist interactions
if (cart) initCartDrawer();
if (wishlist) initWishlistDrawer();
}
/**
* Initialize mobile menu functionality
*/
function initMobileMenu() {
const menuToggle = document.getElementById("mobileMenuToggle");
const navMenu = document.getElementById("navMenu");
if (menuToggle && navMenu) {
menuToggle.addEventListener("click", () => {
navMenu.classList.toggle("active");
menuToggle.classList.toggle("active");
});
// Close menu when clicking outside
document.addEventListener("click", (e) => {
if (
!menuToggle.contains(e.target) &&
!navMenu.contains(e.target) &&
navMenu.classList.contains("active")
) {
navMenu.classList.remove("active");
menuToggle.classList.remove("active");
}
});
}
}
/**
* Initialize cart drawer functionality
*/
function initCartDrawer() {
const cartBtn = document.getElementById("cartBtn");
const cartDrawer = document.getElementById("cartDrawer");
const closeCart = document.getElementById("closeCart");
if (cartBtn && cartDrawer) {
cartBtn.addEventListener("click", () => {
cartDrawer.classList.add("active");
document.body.style.overflow = "hidden";
updateCartUI();
});
if (closeCart) {
closeCart.addEventListener("click", () => {
cartDrawer.classList.remove("active");
document.body.style.overflow = "";
});
}
// Close on overlay click
cartDrawer.addEventListener("click", (e) => {
if (e.target === cartDrawer) {
cartDrawer.classList.remove("active");
document.body.style.overflow = "";
}
});
}
// Initialize checkout button
const checkoutBtn = document.getElementById("checkoutBtn");
if (checkoutBtn) {
checkoutBtn.addEventListener("click", () => {
window.location.href = "/checkout";
});
}
}
/**
* Initialize wishlist drawer functionality
*/
function initWishlistDrawer() {
const wishlistBtn = document.getElementById("wishlistBtn");
const wishlistDrawer = document.getElementById("wishlistDrawer");
const closeWishlist = document.getElementById("closeWishlist");
if (wishlistBtn && wishlistDrawer) {
wishlistBtn.addEventListener("click", () => {
wishlistDrawer.classList.add("active");
document.body.style.overflow = "hidden";
updateWishlistUI();
});
if (closeWishlist) {
closeWishlist.addEventListener("click", () => {
wishlistDrawer.classList.remove("active");
document.body.style.overflow = "";
});
}
// Close on overlay click
wishlistDrawer.addEventListener("click", (e) => {
if (e.target === wishlistDrawer) {
wishlistDrawer.classList.remove("active");
document.body.style.overflow = "";
}
});
}
}
/**
* Update cart UI with current items
*/
function updateCartUI() {
if (typeof CartUtils === "undefined") return;
const cart = CartUtils.getCart();
const cartItems = document.getElementById("cartItems");
const cartTotal = document.getElementById("cartTotal");
const cartBadge = document.getElementById("cartBadge");
if (cartBadge) {
cartBadge.textContent = cart.length;
cartBadge.style.display = cart.length > 0 ? "flex" : "none";
}
if (!cartItems) return;
if (cart.length === 0) {
cartItems.innerHTML = '<p class="empty-cart">Your cart is empty</p>';
if (cartTotal) cartTotal.textContent = "$0.00";
return;
}
cartItems.innerHTML = cart
.map(
(item) => `
<div class="cart-item" data-id="${item.id}">
<img src="${item.image}" alt="${item.name}" class="cart-item-image" />
<div class="cart-item-details">
<h4>${item.name}</h4>
<p class="cart-item-price">${formatPrice(item.price)}</p>
<div class="quantity-controls">
<button class="qty-btn" onclick="decreaseQuantity('${
item.id
}')">-</button>
<span class="quantity">${item.quantity}</span>
<button class="qty-btn" onclick="increaseQuantity('${
item.id
}')">+</button>
</div>
</div>
<button class="remove-item" onclick="removeFromCart('${
item.id
}')" aria-label="Remove item">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
`
)
.join("");
if (cartTotal) {
cartTotal.textContent = formatPrice(CartUtils.getCartTotal());
}
}
/**
* Update wishlist UI with current items
*/
function updateWishlistUI() {
if (typeof WishlistUtils === "undefined") return;
const wishlist = WishlistUtils.getWishlist();
const wishlistItems = document.getElementById("wishlistItems");
const wishlistBadge = document.getElementById("wishlistBadge");
if (wishlistBadge) {
wishlistBadge.textContent = wishlist.length;
wishlistBadge.style.display = wishlist.length > 0 ? "flex" : "none";
}
if (!wishlistItems) return;
if (wishlist.length === 0) {
wishlistItems.innerHTML =
'<p class="empty-wishlist">Your wishlist is empty</p>';
return;
}
wishlistItems.innerHTML = wishlist
.map(
(item) => `
<div class="wishlist-item" data-id="${item.id}">
<img src="${item.image}" alt="${
item.name
}" class="wishlist-item-image" />
<div class="wishlist-item-details">
<h4>${item.name}</h4>
<p class="wishlist-item-price">${formatPrice(item.price)}</p>
<button class="btn-sm btn-primary" onclick="moveToCart('${item.id}')">
Add to Cart
</button>
</div>
<button class="remove-item" onclick="removeFromWishlist('${
item.id
}')" aria-label="Remove from wishlist">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
`
)
.join("");
}
// Global functions for inline event handlers
window.increaseQuantity = (productId) => {
if (typeof CartUtils !== "undefined") {
const cart = CartUtils.getCart();
const item = cart.find((item) => item.id === productId);
if (item) {
CartUtils.updateQuantity(productId, item.quantity + 1);
updateCartUI();
}
}
};
window.decreaseQuantity = (productId) => {
if (typeof CartUtils !== "undefined") {
const cart = CartUtils.getCart();
const item = cart.find((item) => item.id === productId);
if (item && item.quantity > 1) {
CartUtils.updateQuantity(productId, item.quantity - 1);
updateCartUI();
}
}
};
window.removeFromCart = (productId) => {
if (typeof CartUtils !== "undefined") {
CartUtils.removeFromCart(productId);
updateCartUI();
showNotification("Item removed from cart", "info");
}
};
window.removeFromWishlist = (productId) => {
if (typeof WishlistUtils !== "undefined") {
WishlistUtils.removeFromWishlist(productId);
updateWishlistUI();
showNotification("Item removed from wishlist", "info");
}
};
window.moveToCart = (productId) => {
if (
typeof WishlistUtils !== "undefined" &&
typeof CartUtils !== "undefined"
) {
const wishlist = WishlistUtils.getWishlist();
const item = wishlist.find((item) => item.id === productId);
if (item) {
CartUtils.addToCart(item);
WishlistUtils.removeFromWishlist(productId);
updateWishlistUI();
updateCartUI();
showNotification("Item moved to cart", "success");
}
}
};
// Initialize on DOM load
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
// Auto-initialize if data attribute is set
const autoInit = document.body.dataset.autoInitComponents;
if (autoInit !== "false") {
initializeComponents({
activePage: document.body.dataset.activePage || "",
});
}
});
} else {
// Already loaded
const autoInit = document.body.dataset.autoInitComponents;
if (autoInit !== "false") {
initializeComponents({
activePage: document.body.dataset.activePage || "",
});
}
}
// Export for manual initialization
window.Components = Components;
window.initializeComponents = initializeComponents;

View File

@@ -0,0 +1,538 @@
/**
* Customer Authentication State Manager
* Handles checking login status and updating UI accordingly
*/
(function () {
"use strict";
console.log("[customer-auth.js] Loading...");
// Customer authentication state
window.CustomerAuth = {
user: null,
isLoggedIn: false,
isLoading: true,
// Initialize - check session on page load
async init() {
console.log("[CustomerAuth] Initializing...");
await this.checkSession();
this.updateNavbar();
this.setupEventListeners();
},
// Check if user is logged in
async checkSession() {
try {
const response = await fetch("/api/customers/session", {
credentials: "include",
});
if (response.ok) {
const data = await response.json();
if (data.success && data.loggedIn) {
this.user = data.customer;
this.isLoggedIn = true;
console.log("[CustomerAuth] User logged in:", this.user.firstName);
// Sync local cart/wishlist with server
await this.syncCartWithServer();
await this.syncWishlistWithServer();
} else {
this.user = null;
this.isLoggedIn = false;
}
} else {
this.user = null;
this.isLoggedIn = false;
}
} catch (error) {
console.error("[CustomerAuth] Session check failed:", error);
this.user = null;
this.isLoggedIn = false;
}
this.isLoading = false;
},
// Update navbar based on auth state
updateNavbar() {
// Find the sign in link/button in nav-actions
const navActions = document.querySelector(".nav-actions");
if (!navActions) {
console.warn("[CustomerAuth] nav-actions not found");
return;
}
// Find the existing sign in link
const signinLink = navActions.querySelector(
'a[href="/signin"], a[href="/account"]',
);
if (this.isLoggedIn && this.user) {
// User is logged in - update UI
if (signinLink) {
// Replace with user dropdown
const userDropdown = document.createElement("div");
userDropdown.className = "user-dropdown";
userDropdown.innerHTML = `
<button class="nav-icon-btn user-btn" title="Account" id="userDropdownBtn">
<i class="bi bi-person-check-fill"></i>
<span class="user-name-short">${this.escapeHtml(
this.user.firstName,
)}</span>
</button>
<div class="user-dropdown-menu" id="userDropdownMenu">
<div class="user-dropdown-header">
<i class="bi bi-person-circle"></i>
<div class="user-info">
<span class="welcome-text">Welcome back,</span>
<span class="user-name">${this.escapeHtml(
this.user.firstName,
)} ${this.escapeHtml(this.user.lastName || "")}</span>
</div>
</div>
<div class="user-dropdown-divider"></div>
<a href="/account" class="user-dropdown-item">
<i class="bi bi-person"></i> My Account
</a>
<a href="/account#orders" class="user-dropdown-item">
<i class="bi bi-bag-check"></i> My Orders
</a>
<a href="/account#wishlist" class="user-dropdown-item">
<i class="bi bi-heart"></i> My Wishlist
</a>
<div class="user-dropdown-divider"></div>
<button class="user-dropdown-item logout-btn" id="logoutBtn">
<i class="bi bi-box-arrow-right"></i> Sign Out
</button>
</div>
`;
signinLink.replaceWith(userDropdown);
// Add dropdown toggle
const dropdownBtn = document.getElementById("userDropdownBtn");
const dropdownMenu = document.getElementById("userDropdownMenu");
const logoutBtn = document.getElementById("logoutBtn");
if (dropdownBtn && dropdownMenu) {
dropdownBtn.addEventListener("click", (e) => {
e.stopPropagation();
dropdownMenu.classList.toggle("show");
});
// Close dropdown when clicking outside
document.addEventListener("click", () => {
dropdownMenu.classList.remove("show");
});
}
if (logoutBtn) {
logoutBtn.addEventListener("click", () => this.logout());
}
}
} else {
// User is not logged in - ensure sign in link is present
if (signinLink) {
// Update to show sign in icon
signinLink.innerHTML = '<i class="bi bi-person"></i>';
signinLink.href = "/signin";
signinLink.title = "Sign In";
}
}
// Add user dropdown styles if not present
this.addStyles();
},
// Sync local cart with server when logged in
async syncCartWithServer() {
if (!this.isLoggedIn) return;
try {
// Get server cart
const response = await fetch("/api/customers/cart", {
credentials: "include",
});
if (response.ok) {
const data = await response.json();
if (data.success && data.items) {
// Merge with local cart
const localCart = JSON.parse(
localStorage.getItem("skyart_cart") || "[]",
);
// If local cart has items, push them to server
for (const item of localCart) {
const exists = data.items.find((i) => i.productId === item.id);
if (!exists) {
await this.addToServerCart(item);
}
}
// Update local cart with server cart
const mergedCart = data.items.map((item) => ({
id: item.productId,
name: item.name,
price: item.price,
image: item.image,
quantity: item.quantity,
variantColor: item.variantColor,
variantSize: item.variantSize,
}));
localStorage.setItem("skyart_cart", JSON.stringify(mergedCart));
if (window.AppState) {
window.AppState.cart = mergedCart;
window.AppState.updateUI();
}
}
}
} catch (error) {
console.error("[CustomerAuth] Cart sync failed:", error);
}
},
// Sync local wishlist with server
async syncWishlistWithServer() {
if (!this.isLoggedIn) return;
try {
const response = await fetch("/api/customers/wishlist", {
credentials: "include",
});
if (response.ok) {
const data = await response.json();
if (data.success && data.items) {
// Merge with local wishlist
const localWishlist = JSON.parse(
localStorage.getItem("wishlist") || "[]",
);
// Push local items to server
for (const item of localWishlist) {
const exists = data.items.find((i) => i.productId === item.id);
if (!exists) {
await this.addToServerWishlist(item);
}
}
// Update local wishlist with server data
const mergedWishlist = data.items.map((item) => ({
id: item.productId,
name: item.name,
price: item.price,
image: item.image,
}));
localStorage.setItem("wishlist", JSON.stringify(mergedWishlist));
if (window.AppState) {
window.AppState.wishlist = mergedWishlist;
window.AppState.updateUI();
}
}
}
} catch (error) {
console.error("[CustomerAuth] Wishlist sync failed:", error);
}
},
// Add item to server cart
async addToServerCart(item) {
try {
await fetch("/api/customers/cart", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({
productId: item.id,
quantity: item.quantity || 1,
variantColor: item.variantColor,
variantSize: item.variantSize,
}),
});
} catch (error) {
console.error("[CustomerAuth] Add to server cart failed:", error);
}
},
// Add item to server wishlist
async addToServerWishlist(item) {
try {
await fetch("/api/customers/wishlist", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ productId: item.id }),
});
} catch (error) {
console.error("[CustomerAuth] Add to server wishlist failed:", error);
}
},
// Setup event listeners for cart/wishlist changes
setupEventListeners() {
// Listen for cart changes to sync with server
window.addEventListener("cart-updated", async (e) => {
if (this.isLoggedIn && e.detail) {
// Debounce sync
clearTimeout(this._syncCartTimeout);
this._syncCartTimeout = setTimeout(() => {
this.syncCartChanges(e.detail);
}, 500);
}
});
// Listen for wishlist changes
window.addEventListener("wishlist-updated", async (e) => {
if (this.isLoggedIn && e.detail) {
clearTimeout(this._syncWishlistTimeout);
this._syncWishlistTimeout = setTimeout(() => {
this.syncWishlistChanges(e.detail);
}, 500);
}
});
},
// Sync cart changes with server
async syncCartChanges(cart) {
if (!this.isLoggedIn) return;
// For now, just ensure items are added/removed
// More sophisticated sync could be implemented
},
// Sync wishlist changes with server
async syncWishlistChanges(wishlist) {
if (!this.isLoggedIn) return;
// Sync wishlist changes
},
// Logout
async logout() {
try {
const response = await fetch("/api/customers/logout", {
method: "POST",
credentials: "include",
});
if (response.ok) {
this.user = null;
this.isLoggedIn = false;
// Show notification
if (window.AppState && window.AppState.showNotification) {
window.AppState.showNotification(
"You have been signed out",
"info",
);
}
// Redirect to home or refresh
window.location.href = "/home";
}
} catch (error) {
console.error("[CustomerAuth] Logout failed:", error);
}
},
// Helper: Escape HTML
escapeHtml(text) {
if (!text) return "";
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
},
// Add styles for user dropdown
addStyles() {
if (document.getElementById("customer-auth-styles")) return;
const style = document.createElement("style");
style.id = "customer-auth-styles";
style.textContent = `
.user-dropdown {
position: relative;
}
.user-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px !important;
border-radius: 20px;
background: var(--primary-pink-light, #fff5f7);
transition: all 0.2s ease;
}
.user-btn:hover {
background: var(--primary-pink, #ff85a2);
color: white;
}
.user-btn i {
font-size: 1.1rem;
color: var(--primary-pink, #ff85a2);
}
.user-btn:hover i {
color: white;
}
.user-name-short {
font-size: 0.85rem;
font-weight: 600;
color: var(--text-primary, #333);
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.user-btn:hover .user-name-short {
color: white;
}
.user-dropdown-menu {
position: absolute;
top: calc(100% + 10px);
right: 0;
min-width: 220px;
background: white;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.15);
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: all 0.2s ease;
z-index: 1000;
overflow: hidden;
}
.user-dropdown-menu.show {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.user-dropdown-header {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
background: var(--primary-pink-light, #fff5f7);
}
.user-dropdown-header > i {
font-size: 2rem;
color: var(--primary-pink, #ff85a2);
}
.user-dropdown-header .user-info {
display: flex;
flex-direction: column;
}
.user-dropdown-header .welcome-text {
font-size: 0.75rem;
color: var(--text-light, #999);
}
.user-dropdown-header .user-name {
font-size: 0.9rem;
font-weight: 600;
color: var(--text-primary, #333);
}
.user-dropdown-divider {
height: 1px;
background: var(--border-light, #eee);
margin: 4px 0;
}
.user-dropdown-item {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 16px;
color: var(--text-primary, #333);
text-decoration: none;
font-size: 0.9rem;
transition: all 0.2s ease;
cursor: pointer;
border: none;
background: none;
width: 100%;
text-align: left;
}
.user-dropdown-item:hover {
background: var(--primary-pink-light, #fff5f7);
color: var(--primary-pink, #ff85a2);
}
.user-dropdown-item i {
font-size: 1rem;
width: 20px;
text-align: center;
}
.logout-btn {
color: #e74c3c;
}
.logout-btn:hover {
background: #fdf2f2;
color: #c0392b;
}
@media (max-width: 768px) {
.user-name-short {
display: none;
}
.user-btn {
padding: 8px !important;
border-radius: 50%;
}
.user-dropdown {
-webkit-transform: translateZ(0);
transform: translateZ(0);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.user-dropdown-menu {
right: -10px;
min-width: 200px;
position: fixed;
top: 70px;
right: 10px;
-webkit-transform: translateZ(0);
transform: translateZ(0);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
will-change: opacity, visibility;
}
.user-dropdown-menu.show {
transform: translateZ(0);
}
}
`;
document.head.appendChild(style);
},
};
// Initialize on DOM ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
window.CustomerAuth.init();
});
} else {
window.CustomerAuth.init();
}
})();

View File

@@ -0,0 +1,228 @@
/**
* Dynamic Page Content Loader
* Loads page content from the API and renders it with proper formatting
*/
// Convert Quill Delta to HTML - accurate conversion matching backend format
function convertDeltaToHtml(delta) {
if (!delta || !delta.ops) return "";
let html = "";
let currentLine = "";
let inListType = null; // 'bullet' or 'ordered'
const ops = delta.ops;
for (let i = 0; i < ops.length; i++) {
const op = ops[i];
const nextOp = ops[i + 1];
if (typeof op.insert === "string") {
const text = op.insert;
const inlineAttrs = op.attributes || {};
// Check if this is a standalone newline with block attributes
if (text === "\n") {
const blockAttrs = inlineAttrs;
// Handle list transitions
if (blockAttrs.list) {
const newListType = blockAttrs.list;
if (inListType !== newListType) {
if (inListType) {
html += inListType === "ordered" ? "</ol>" : "</ul>";
}
html += newListType === "ordered" ? "<ol>" : "<ul>";
inListType = newListType;
}
html += `<li>${currentLine}</li>`;
} else {
// Close any open list
if (inListType) {
html += inListType === "ordered" ? "</ol>" : "</ul>";
inListType = null;
}
// Apply block formatting
if (blockAttrs.header) {
html += `<h${blockAttrs.header}>${currentLine}</h${blockAttrs.header}>`;
} else if (blockAttrs.blockquote) {
html += `<blockquote>${currentLine}</blockquote>`;
} else if (blockAttrs["code-block"]) {
html += `<pre><code>${currentLine}</code></pre>`;
} else if (currentLine) {
html += `<p>${currentLine}</p>`;
}
}
currentLine = "";
} else {
// Regular text - may contain embedded newlines
const parts = text.split("\n");
for (let j = 0; j < parts.length; j++) {
const part = parts[j];
// Format the text part
if (part.length > 0) {
let formatted = escapeHtml(part);
// Apply inline formatting
if (inlineAttrs.bold) formatted = `<strong>${formatted}</strong>`;
if (inlineAttrs.italic) formatted = `<em>${formatted}</em>`;
if (inlineAttrs.underline) formatted = `<u>${formatted}</u>`;
if (inlineAttrs.strike) formatted = `<s>${formatted}</s>`;
if (inlineAttrs.code) formatted = `<code>${formatted}</code>`;
if (inlineAttrs.link)
formatted = `<a href="${inlineAttrs.link}" target="_blank" rel="noopener">${formatted}</a>`;
currentLine += formatted;
}
// Handle embedded newlines (not the last part)
if (j < parts.length - 1) {
// Close any open list for embedded newlines
if (inListType) {
html += inListType === "ordered" ? "</ol>" : "</ul>";
inListType = null;
}
if (currentLine) {
html += `<p>${currentLine}</p>`;
}
currentLine = "";
}
}
}
} else if (op.insert && op.insert.image) {
// Flush pending content
if (currentLine) {
if (inListType) {
html += `<li>${currentLine}</li>`;
html += inListType === "ordered" ? "</ol>" : "</ul>";
inListType = null;
} else {
html += `<p>${currentLine}</p>`;
}
currentLine = "";
}
html += `<img src="${op.insert.image}" class="content-image" alt="Content image">`;
}
}
// Flush remaining content
if (inListType) {
if (currentLine) html += `<li>${currentLine}</li>`;
html += inListType === "ordered" ? "</ol>" : "</ul>";
} else if (currentLine) {
html += `<p>${currentLine}</p>`;
}
return html;
}
function escapeHtml(text) {
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
// Parse and render page content (handles both Delta JSON and raw HTML)
function parsePageContent(content) {
if (!content) return "<p>Content coming soon...</p>";
try {
const delta = JSON.parse(content);
if (delta.ops) {
return convertDeltaToHtml(delta);
}
return content;
} catch (e) {
// Not JSON, return as-is (probably HTML)
return content;
}
}
// Load page content from API
async function loadPageContent(slug, options = {}) {
const {
titleSelector = "#pageTitle",
contentSelector = "#dynamicContent",
staticSelector = "#staticContent",
showLoading = true,
} = options;
const dynamicContent = document.querySelector(contentSelector);
const staticContent = document.querySelector(staticSelector);
const titleElement = document.querySelector(titleSelector);
if (!dynamicContent) {
console.warn("Dynamic content container not found:", contentSelector);
return null;
}
if (showLoading) {
dynamicContent.innerHTML = `
<div style="text-align: center; padding: 40px;">
<div class="loading-spinner" style="width: 40px; height: 40px; border: 3px solid rgba(252,177,216,0.2); border-top-color: #FCB1D8; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto;"></div>
<p style="margin-top: 16px; color: #888;">Loading content...</p>
</div>
`;
}
try {
const response = await fetch(`/api/pages/${slug}`);
const data = await response.json();
if (data.success && data.page) {
const page = data.page;
// Update page title if provided
if (page.title && titleElement) {
titleElement.textContent = page.title;
}
// Parse and render content
const htmlContent = parsePageContent(page.content);
dynamicContent.innerHTML = htmlContent;
// Hide static fallback if exists
if (staticContent) {
staticContent.style.display = "none";
}
return page;
} else {
// Show static fallback
dynamicContent.style.display = "none";
if (staticContent) {
staticContent.style.display = "block";
}
return null;
}
} catch (error) {
console.error("Failed to load page content:", error);
dynamicContent.style.display = "none";
if (staticContent) {
staticContent.style.display = "block";
}
return null;
}
}
// Auto-initialize on DOMContentLoaded if data-page-slug is set
document.addEventListener("DOMContentLoaded", () => {
const pageContainer = document.querySelector("[data-page-slug]");
if (pageContainer) {
const slug = pageContainer.dataset.pageSlug;
loadPageContent(slug);
}
});
// Export for use in other scripts
window.DynamicPage = {
loadPageContent,
parsePageContent,
convertDeltaToHtml,
};

View File

@@ -0,0 +1,114 @@
/**
* Mobile Touch Optimization
* Eliminates double-tap behavior and ensures immediate response on mobile devices
*/
(function () {
"use strict";
// Only apply on touch devices
if (!("ontouchstart" in window)) return;
// Disable 300ms click delay on mobile
let touchStartX, touchStartY;
// Add fastclick functionality for immediate response
document.addEventListener(
"touchstart",
function (e) {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
},
{ passive: true },
);
document.addEventListener(
"touchend",
function (e) {
if (!touchStartX || !touchStartY) return;
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
// Check if it's a tap (not a swipe)
const diffX = Math.abs(touchEndX - touchStartX);
const diffY = Math.abs(touchEndY - touchStartY);
if (diffX < 10 && diffY < 10) {
// This is a tap, ensure immediate response
const target = e.target.closest(
"a, button, [data-bs-toggle], .product-card, .portfolio-card, .blog-card, .nav-link, .btn",
);
if (target && !target.disabled) {
// Add visual feedback
target.style.opacity = "0.7";
setTimeout(() => {
target.style.opacity = "";
}, 100);
}
}
touchStartX = null;
touchStartY = null;
},
{ passive: false },
);
// Remove hover states that cause double-tap on mobile
if (window.matchMedia("(hover: none)").matches) {
const style = document.createElement("style");
style.textContent = `
/* Override hover states for immediate touch response */
.product-card, .portfolio-card, .blog-card, .btn, .nav-link {
transition: transform 0.1s ease, opacity 0.1s ease !important;
}
`;
document.head.appendChild(style);
}
// Optimize click handlers for mobile
function optimizeClickHandler(selector) {
const elements = document.querySelectorAll(selector);
elements.forEach((el) => {
if (el.dataset.mobileOptimized) return;
el.dataset.mobileOptimized = "true";
// Remove any existing event listeners that might cause delays
el.style.pointerEvents = "auto";
el.style.touchAction = "manipulation";
// Ensure proper z-index for touch
const computedStyle = window.getComputedStyle(el);
if (computedStyle.position === "static") {
el.style.position = "relative";
}
});
}
// Apply optimizations when DOM is ready
document.addEventListener("DOMContentLoaded", function () {
optimizeClickHandler(
"a, button, .product-card, .portfolio-card, .blog-card, .btn, .nav-link, .filter-btn, .add-to-cart-btn",
);
});
// Apply optimizations to dynamically added content
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.type === "childList") {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === Node.ELEMENT_NODE) {
optimizeClickHandler(
"a, button, .product-card, .portfolio-card, .blog-card, .btn, .nav-link, .filter-btn, .add-to-cart-btn",
);
}
});
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,332 @@
/**
* Shared Cart and Wishlist Utilities
* Extracted from duplicated code across multiple pages
*/
const CartUtils = {
/**
* Get cart from localStorage
* @returns {Array} Cart items
*/
getCart() {
try {
return JSON.parse(localStorage.getItem("skyart_cart") || "[]");
} catch (error) {
console.error("Error loading cart:", error);
return [];
}
},
/**
* Save cart to localStorage
* @param {Array} cart - Cart items
*/
saveCart(cart) {
try {
localStorage.setItem("skyart_cart", JSON.stringify(cart));
this.updateCartBadge();
} catch (error) {
console.error("Error saving cart:", error);
}
},
/**
* Add item to cart
* @param {Object} product - Product to add
* @param {number} quantity - Quantity to add
* @param {string} variant - Selected variant
*/
addToCart(product, quantity = 1, variant = null) {
const cart = this.getCart();
const cartKey = variant ? `${product.id}-${variant}` : product.id;
const existingIndex = cart.findIndex((item) => {
const itemKey = item.variant
? `${item.productId}-${item.variant}`
: item.productId;
return itemKey === cartKey;
});
if (existingIndex >= 0) {
cart[existingIndex].quantity += quantity;
} else {
cart.push({
productId: product.id,
name: product.name,
price: product.price,
image: product.images?.[0]?.image_url || product.imageurl,
quantity,
variant,
slug: product.slug,
});
}
this.saveCart(cart);
return cart;
},
/**
* Remove item from cart
* @param {string} productId - Product ID to remove
* @param {string} variant - Variant to remove
*/
removeFromCart(productId, variant = null) {
const cart = this.getCart();
const cartKey = variant ? `${productId}-${variant}` : productId;
const filtered = cart.filter((item) => {
const itemKey = item.variant
? `${item.productId}-${item.variant}`
: item.productId;
return itemKey !== cartKey;
});
this.saveCart(filtered);
return filtered;
},
/**
* Update cart item quantity
* @param {string} productId - Product ID
* @param {number} quantity - New quantity
* @param {string} variant - Variant
*/
updateQuantity(productId, quantity, variant = null) {
const cart = this.getCart();
const cartKey = variant ? `${productId}-${variant}` : productId;
const index = cart.findIndex((item) => {
const itemKey = item.variant
? `${item.productId}-${item.variant}`
: item.productId;
return itemKey === cartKey;
});
if (index >= 0) {
if (quantity <= 0) {
cart.splice(index, 1);
} else {
cart[index].quantity = quantity;
}
this.saveCart(cart);
}
return cart;
},
/**
* Get cart total
* @returns {number} Total price
*/
getCartTotal() {
const cart = this.getCart();
return cart.reduce((total, item) => {
return total + parseFloat(item.price) * item.quantity;
}, 0);
},
/**
* Update cart badge count
*/
updateCartBadge() {
const cart = this.getCart();
const count = cart.reduce((sum, item) => sum + item.quantity, 0);
const badge = document.querySelector(".cart-badge");
if (badge) {
badge.textContent = count;
badge.style.display = count > 0 ? "flex" : "none";
}
},
/**
* Clear cart
*/
clearCart() {
localStorage.removeItem("skyart_cart");
this.updateCartBadge();
},
};
const WishlistUtils = {
/**
* Get wishlist from localStorage
* @returns {Array} Wishlist items
*/
getWishlist() {
try {
return JSON.parse(localStorage.getItem("wishlist") || "[]");
} catch (error) {
console.error("Error loading wishlist:", error);
return [];
}
},
/**
* Save wishlist to localStorage
* @param {Array} wishlist - Wishlist items
*/
saveWishlist(wishlist) {
try {
localStorage.setItem("wishlist", JSON.stringify(wishlist));
this.updateWishlistBadge();
} catch (error) {
console.error("Error saving wishlist:", error);
}
},
/**
* Add item to wishlist
* @param {Object} product - Product to add
*/
addToWishlist(product) {
const wishlist = this.getWishlist();
if (!wishlist.find((item) => item.id === product.id)) {
wishlist.push({
id: product.id,
name: product.name,
price: product.price,
image: product.images?.[0]?.image_url || product.imageurl,
slug: product.slug,
});
this.saveWishlist(wishlist);
}
return wishlist;
},
/**
* Remove item from wishlist
* @param {string} productId - Product ID to remove
*/
removeFromWishlist(productId) {
const wishlist = this.getWishlist();
const filtered = wishlist.filter((item) => item.id !== productId);
this.saveWishlist(filtered);
return filtered;
},
/**
* Check if product is in wishlist
* @param {string} productId - Product ID
* @returns {boolean} True if in wishlist
*/
isInWishlist(productId) {
const wishlist = this.getWishlist();
return wishlist.some((item) => item.id === productId);
},
/**
* Update wishlist badge count
*/
updateWishlistBadge() {
const wishlist = this.getWishlist();
const badge = document.querySelector(".wishlist-badge");
if (badge) {
badge.textContent = wishlist.length;
badge.style.display = wishlist.length > 0 ? "flex" : "none";
}
},
/**
* Clear wishlist
*/
clearWishlist() {
localStorage.removeItem("wishlist");
this.updateWishlistBadge();
},
};
/**
* Format price for display
* @param {number} price - Price to format
* @returns {string} Formatted price
*/
const formatPrice = (price) => {
return `$${parseFloat(price).toFixed(2)}`;
};
/**
* Format date for display
* @param {string} dateString - ISO date string
* @returns {string} Formatted date
*/
const formatDate = (dateString) => {
const date = new Date(dateString);
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
};
/**
* Debounce function
* @param {Function} func - Function to debounce
* @param {number} wait - Wait time in ms
* @returns {Function} Debounced function
*/
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
/**
* Show notification toast
* @param {string} message - Message to display
* @param {string} type - Type: success, error, info
*/
const showNotification = (message, type = "success") => {
// Check if a notification container exists
let container = document.querySelector(".notification-container");
if (!container) {
container = document.createElement("div");
container.className = "notification-container";
container.style.cssText = `
position: fixed;
top: 100px;
right: 20px;
z-index: 10000;
`;
document.body.appendChild(container);
}
const notification = document.createElement("div");
notification.className = `notification notification-${type}`;
notification.style.cssText = `
background: ${
type === "success" ? "#10b981" : type === "error" ? "#ef4444" : "#3b82f6"
};
color: white;
padding: 12px 20px;
border-radius: 8px;
margin-bottom: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
animation: slideIn 0.3s ease-out;
`;
notification.textContent = message;
container.appendChild(notification);
setTimeout(() => {
notification.style.animation = "slideOut 0.3s ease-out";
setTimeout(() => notification.remove(), 300);
}, 3000);
};
// Export for use in other scripts
if (typeof window !== "undefined") {
window.CartUtils = CartUtils;
window.WishlistUtils = WishlistUtils;
window.formatPrice = formatPrice;
window.formatDate = formatDate;
window.debounce = debounce;
window.showNotification = showNotification;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,791 @@
<!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="Checkout - Sky Art Shop" />
<title>Checkout - Sky Art Shop</title>
<!-- Fonts -->
<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=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap"
rel="stylesheet"
/>
<!-- Icons -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
/>
<!-- Modern Theme CSS -->
<link rel="stylesheet" href="/assets/css/modern-theme.css?v=20260118drawer" />
<link rel="stylesheet" href="/assets/css/mobile-fixes.css?v=20260118c" />
<style>
.checkout-container {
max-width: 1200px;
margin: 0 auto;
padding: var(--spacing-xl) var(--spacing-lg);
}
.checkout-grid {
display: grid;
grid-template-columns: 1fr 400px;
gap: var(--spacing-xl);
}
@media (max-width: 992px) {
.checkout-grid {
grid-template-columns: 1fr;
}
}
.checkout-section {
background: var(--bg-white);
border-radius: var(--radius-lg);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
margin-bottom: var(--spacing-lg);
}
.checkout-section h2 {
font-size: 1.25rem;
margin-bottom: var(--spacing-lg);
color: var(--text-primary);
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.checkout-section h2 i {
color: var(--primary-pink);
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-md);
}
@media (max-width: 576px) {
.form-row {
grid-template-columns: 1fr;
}
}
.form-group {
margin-bottom: var(--spacing-md);
}
.form-group label {
display: block;
margin-bottom: var(--spacing-xs);
font-weight: 500;
color: var(--text-secondary);
font-size: 0.9rem;
}
.form-group input,
.form-group select {
width: 100%;
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--border-light);
border-radius: var(--radius-md);
font-size: 1rem;
transition: var(--transition-fast);
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: var(--primary-pink);
box-shadow: 0 0 0 3px var(--primary-pink-light);
}
.order-summary {
position: sticky;
top: 100px;
}
.summary-items {
max-height: 300px;
overflow-y: auto;
margin-bottom: var(--spacing-lg);
}
.summary-item {
display: flex;
gap: var(--spacing-md);
padding: var(--spacing-sm) 0;
border-bottom: 1px solid var(--border-light);
}
.summary-item:last-child {
border-bottom: none;
}
.summary-item-image {
width: 60px;
height: 60px;
border-radius: var(--radius-md);
object-fit: cover;
background: var(--bg-light);
}
.summary-item-details {
flex: 1;
}
.summary-item-name {
font-weight: 600;
color: var(--text-primary);
font-size: 0.9rem;
}
.summary-item-variant {
font-size: 0.8rem;
color: var(--text-light);
}
.summary-item-qty {
font-size: 0.85rem;
color: var(--text-secondary);
}
.summary-item-price {
font-weight: 600;
color: var(--text-primary);
}
.summary-totals {
border-top: 2px solid var(--border-light);
padding-top: var(--spacing-md);
}
.summary-row {
display: flex;
justify-content: space-between;
margin-bottom: var(--spacing-sm);
color: var(--text-secondary);
}
.summary-row.total {
font-size: 1.25rem;
font-weight: 700;
color: var(--text-primary);
margin-top: var(--spacing-md);
padding-top: var(--spacing-md);
border-top: 2px solid var(--text-primary);
}
.checkout-btn {
width: 100%;
padding: var(--spacing-md);
font-size: 1.1rem;
margin-top: var(--spacing-lg);
}
.empty-cart-message {
text-align: center;
padding: var(--spacing-xl);
}
.empty-cart-message i {
font-size: 4rem;
color: var(--text-light);
margin-bottom: var(--spacing-md);
}
.empty-cart-message h3 {
margin-bottom: var(--spacing-md);
color: var(--text-primary);
}
.payment-methods {
display: flex;
gap: var(--spacing-md);
flex-wrap: wrap;
}
.payment-method {
flex: 1;
min-width: 150px;
padding: var(--spacing-md);
border: 2px solid var(--border-light);
border-radius: var(--radius-md);
cursor: pointer;
text-align: center;
transition: var(--transition-fast);
}
.payment-method:hover,
.payment-method.active {
border-color: var(--primary-pink);
background: var(--primary-pink-light);
}
.payment-method i {
font-size: 1.5rem;
margin-bottom: var(--spacing-xs);
display: block;
}
.secure-notice {
display: flex;
align-items: center;
gap: var(--spacing-sm);
padding: var(--spacing-md);
background: var(--bg-light);
border-radius: var(--radius-md);
margin-top: var(--spacing-lg);
font-size: 0.85rem;
color: var(--text-secondary);
}
.secure-notice i {
color: var(--success);
font-size: 1.2rem;
}
</style>
</head>
<body>
<!-- Sticky Banner Wrapper -->
<div class="sticky-banner-wrapper">
<!-- Announcement Bar -->
<div class="announcement-bar">
<div class="container">
<span id="announcement-text"
>🌸 Free shipping on orders over $50! Use code: SPRING2026</span
>
</div>
</div>
<!-- Navigation -->
<nav class="navbar" id="navbar">
<div class="container">
<a href="/" class="nav-logo">
<span class="logo-icon"></span>
<span>Sky Art Shop</span>
</a>
<button
class="mobile-menu-btn"
aria-label="Toggle menu"
aria-expanded="false"
>
<i class="bi bi-list"></i>
</button>
<div class="nav-menu">
<ul class="nav-links" id="navLinks">
<!-- Menu items will be loaded dynamically -->
</ul>
</div>
<div class="nav-actions">
<a href="#" class="nav-action" id="searchToggle" title="Search">
<i class="bi bi-search"></i>
</a>
<a href="#" class="nav-action wishlist-toggle" title="Wishlist">
<i class="bi bi-heart"></i>
<span class="wishlist-badge">0</span>
</a>
<a href="#" class="nav-action cart-toggle" title="Cart">
<i class="bi bi-bag"></i>
<span class="cart-badge">0</span>
</a>
<a href="#" class="nav-action" id="userMenuToggle" title="Account">
<i class="bi bi-person"></i>
</a>
</div>
</div>
</nav>
</div>
<!-- Search Overlay -->
<div class="search-overlay">
<div class="search-container">
<form class="search-form" action="/shop" method="get">
<input
type="search"
name="search"
placeholder="Search for products..."
class="search-input"
id="searchInput"
/>
<button type="submit" class="search-submit">
<i class="bi bi-search"></i>
</button>
</form>
<button class="search-close"><i class="bi bi-x"></i></button>
</div>
</div>
<!-- Page Header -->
<section class="page-header" style="padding: var(--spacing-xl) 0">
<div class="container text-center">
<h1>Checkout</h1>
<p class="section-subtitle">Complete your order</p>
</div>
</section>
<!-- Checkout Content -->
<main class="checkout-container">
<div id="checkoutContent">
<!-- Will be populated by JavaScript based on cart contents -->
<div class="checkout-grid">
<!-- Left Column - Forms -->
<div class="checkout-forms">
<!-- Contact Information -->
<div class="checkout-section">
<h2><i class="bi bi-person-circle"></i> Contact Information</h2>
<div class="form-group">
<label for="email">Email Address</label>
<input
type="email"
id="email"
placeholder="your@email.com"
required
/>
</div>
<div class="form-row">
<div class="form-group">
<label for="firstName">First Name</label>
<input
type="text"
id="firstName"
placeholder="John"
required
/>
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" placeholder="Doe" required />
</div>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" placeholder="+1 (555) 123-4567" />
</div>
</div>
<!-- Shipping Address -->
<div class="checkout-section">
<h2><i class="bi bi-truck"></i> Shipping Address</h2>
<div class="form-group">
<label for="address">Street Address</label>
<input
type="text"
id="address"
placeholder="123 Creative Lane"
required
/>
</div>
<div class="form-group">
<label for="address2">Apartment, suite, etc. (optional)</label>
<input type="text" id="address2" placeholder="Apt 4B" />
</div>
<div class="form-row">
<div class="form-group">
<label for="city">City</label>
<input
type="text"
id="city"
placeholder="New York"
required
/>
</div>
<div class="form-group">
<label for="state">State/Province</label>
<input type="text" id="state" placeholder="NY" required />
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="zip">ZIP/Postal Code</label>
<input type="text" id="zip" placeholder="10001" required />
</div>
<div class="form-group">
<label for="country">Country</label>
<select id="country" required>
<option value="US">United States</option>
<option value="CA">Canada</option>
<option value="GB">United Kingdom</option>
<option value="AU">Australia</option>
<option value="DE">Germany</option>
<option value="FR">France</option>
</select>
</div>
</div>
</div>
<!-- Payment Method -->
<div class="checkout-section">
<h2><i class="bi bi-credit-card"></i> Payment Method</h2>
<div class="payment-methods">
<div class="payment-method active" data-method="card">
<i class="bi bi-credit-card-2-front"></i>
<span>Credit Card</span>
</div>
<div class="payment-method" data-method="paypal">
<i class="bi bi-paypal"></i>
<span>PayPal</span>
</div>
</div>
<div id="cardPaymentForm" style="margin-top: var(--spacing-lg)">
<div class="form-group">
<label for="cardNumber">Card Number</label>
<input
type="text"
id="cardNumber"
placeholder="1234 5678 9012 3456"
maxlength="19"
/>
</div>
<div class="form-row">
<div class="form-group">
<label for="expiry">Expiry Date</label>
<input
type="text"
id="expiry"
placeholder="MM/YY"
maxlength="5"
/>
</div>
<div class="form-group">
<label for="cvv">CVV</label>
<input
type="text"
id="cvv"
placeholder="123"
maxlength="4"
/>
</div>
</div>
<div class="form-group">
<label for="cardName">Name on Card</label>
<input type="text" id="cardName" placeholder="John Doe" />
</div>
</div>
<div class="secure-notice">
<i class="bi bi-shield-lock-fill"></i>
<span
>Your payment information is encrypted and secure. We never
store your full card details.</span
>
</div>
</div>
</div>
<!-- Right Column - Order Summary -->
<div class="order-summary-wrapper">
<div class="checkout-section order-summary">
<h2><i class="bi bi-bag-check"></i> Order Summary</h2>
<div class="summary-items" id="summaryItems">
<!-- Items will be populated by JavaScript -->
</div>
<div class="summary-totals">
<div class="summary-row">
<span>Subtotal</span>
<span id="summarySubtotal">$0.00</span>
</div>
<div class="summary-row">
<span>Shipping</span>
<span id="summaryShipping">Calculated at next step</span>
</div>
<div class="summary-row">
<span>Tax</span>
<span id="summaryTax">$0.00</span>
</div>
<div class="summary-row total">
<span>Total</span>
<span id="summaryTotal">$0.00</span>
</div>
</div>
<button
class="btn btn-primary checkout-btn"
id="placeOrderBtn"
disabled
>
<i class="bi bi-lock-fill"></i> Place Order
</button>
</div>
</div>
</div>
<!-- Empty Cart State -->
<div
class="empty-cart-message"
id="emptyCartMessage"
style="display: none"
>
<i class="bi bi-cart-x"></i>
<h3>Your cart is empty</h3>
<p>Add some items to your cart before checking out.</p>
<a
href="/shop"
class="btn btn-primary"
style="margin-top: var(--spacing-md)"
>
<i class="bi bi-arrow-left"></i> Continue Shopping
</a>
</div>
</div>
</main>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-column">
<h4>Sky Art Shop</h4>
<p>
Your creative journey starts here. Quality supplies for every
artist.
</p>
</div>
<div class="footer-column">
<h4>Quick Links</h4>
<ul class="footer-links">
<li><a href="/shop">Shop</a></li>
<li><a href="/about">About Us</a></li>
<li><a href="/contact">Contact</a></li>
<li><a href="/faq">FAQ</a></li>
</ul>
</div>
<div class="footer-column">
<h4>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="/privacy">Privacy Policy</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Contact Us</h4>
<ul class="footer-links">
<li><i class="bi bi-envelope"></i> hello@skyartshop.com</li>
<li><i class="bi bi-telephone"></i> (555) 123-4567</li>
<li><i class="bi bi-geo-alt"></i> 123 Creative Lane</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2026 Sky Art Shop. All rights reserved.</p>
<p>
Made with
<i class="bi bi-heart-fill" style="color: var(--primary-pink)"></i>
for creative souls
</p>
</div>
</div>
</footer>
<!-- Cart Drawer -->
<div class="cart-overlay"></div>
<div class="cart-drawer">
<div class="cart-header">
<h3>Shopping Cart</h3>
<button class="cart-close"><i class="bi bi-x"></i></button>
</div>
<div class="cart-items"></div>
<div class="cart-footer">
<div class="cart-total">
<span>Total:</span>
<span class="cart-total-amount">$0.00</span>
</div>
<a href="/checkout" class="btn btn-primary cart-checkout-btn"
>Checkout</a
>
</div>
</div>
<!-- Wishlist Drawer -->
<div class="wishlist-overlay"></div>
<div class="wishlist-drawer">
<div class="wishlist-header">
<h3><i class="bi bi-heart"></i> My Wishlist</h3>
<button class="wishlist-close"><i class="bi bi-x"></i></button>
</div>
<div class="wishlist-items"></div>
<div class="wishlist-footer">
<a href="/shop" class="btn btn-outline">Continue Shopping</a>
</div>
</div>
<!-- Scripts -->
<script src="/assets/js/modern-theme.js?v=20260118c"></script>
<script src="/assets/js/customer-auth.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
loadCheckoutPage();
setupPaymentMethods();
setupFormValidation();
});
function loadCheckoutPage() {
const cart = JSON.parse(localStorage.getItem("cart") || "[]");
const checkoutGrid = document.querySelector(".checkout-grid");
const emptyMessage = document.getElementById("emptyCartMessage");
if (cart.length === 0) {
checkoutGrid.style.display = "none";
emptyMessage.style.display = "block";
return;
}
checkoutGrid.style.display = "grid";
emptyMessage.style.display = "none";
renderOrderSummary(cart);
}
function renderOrderSummary(cart) {
const summaryItems = document.getElementById("summaryItems");
let subtotal = 0;
const itemsHtml = cart
.map((item) => {
const itemTotal = parseFloat(item.price) * item.quantity;
subtotal += itemTotal;
return `
<div class="summary-item">
<img
src="${
item.image || "/assets/images/products/placeholder.jpg"
}"
alt="${item.name}"
class="summary-item-image"
onerror="this.src='/assets/images/products/placeholder.jpg'"
/>
<div class="summary-item-details">
<div class="summary-item-name">${item.name}</div>
${
item.variant
? `<div class="summary-item-variant">${item.variant}</div>`
: ""
}
<div class="summary-item-qty">Qty: ${item.quantity}</div>
</div>
<div class="summary-item-price">$${itemTotal.toFixed(2)}</div>
</div>
`;
})
.join("");
summaryItems.innerHTML = itemsHtml;
// Calculate totals
const shipping = subtotal >= 50 ? 0 : 5.99;
const tax = subtotal * 0.08; // 8% tax
const total = subtotal + shipping + tax;
document.getElementById(
"summarySubtotal"
).textContent = `$${subtotal.toFixed(2)}`;
document.getElementById("summaryShipping").textContent =
shipping === 0 ? "FREE" : `$${shipping.toFixed(2)}`;
document.getElementById("summaryTax").textContent = `$${tax.toFixed(
2
)}`;
document.getElementById("summaryTotal").textContent = `$${total.toFixed(
2
)}`;
// Enable place order button
document.getElementById("placeOrderBtn").disabled = false;
}
function setupPaymentMethods() {
const paymentMethods = document.querySelectorAll(".payment-method");
const cardForm = document.getElementById("cardPaymentForm");
paymentMethods.forEach((method) => {
method.addEventListener("click", () => {
paymentMethods.forEach((m) => m.classList.remove("active"));
method.classList.add("active");
if (method.dataset.method === "card") {
cardForm.style.display = "block";
} else {
cardForm.style.display = "none";
}
});
});
}
function setupFormValidation() {
const placeOrderBtn = document.getElementById("placeOrderBtn");
placeOrderBtn.addEventListener("click", (e) => {
e.preventDefault();
// Basic validation
const requiredFields = [
"email",
"firstName",
"lastName",
"address",
"city",
"state",
"zip",
];
let isValid = true;
requiredFields.forEach((fieldId) => {
const field = document.getElementById(fieldId);
if (!field.value.trim()) {
field.style.borderColor = "var(--error)";
isValid = false;
} else {
field.style.borderColor = "";
}
});
if (!isValid) {
alert("Please fill in all required fields.");
return;
}
// Show success message (in a real app, this would submit to the server)
alert(
"Thank you for your order! This is a demo - no actual order was placed."
);
// Clear cart
localStorage.removeItem("cart");
window.location.href = "/";
});
// Format card number with spaces
const cardNumberInput = document.getElementById("cardNumber");
cardNumberInput.addEventListener("input", (e) => {
let value = e.target.value.replace(/\s/g, "").replace(/\D/g, "");
value = value.match(/.{1,4}/g)?.join(" ") || value;
e.target.value = value;
});
// Format expiry date
const expiryInput = document.getElementById("expiry");
expiryInput.addEventListener("input", (e) => {
let value = e.target.value.replace(/\D/g, "");
if (value.length >= 2) {
value = value.substring(0, 2) + "/" + value.substring(2);
}
e.target.value = value;
});
}
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

265
website/public/faq-old.html Normal file
View File

@@ -0,0 +1,265 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FAQ - Sky Art Shop</title>
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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" />
</head>
<body>
<script>window.__bodyReady=true</script>
<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 active">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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1><i class="bi bi-question-circle"></i> Frequently Asked Questions</h1>
<p class="hero-subtitle">Quick answers to common questions</p>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<div class="container">
<div style="max-width: 800px; margin: 0 auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">How long does shipping take?</h3>
<p style="color: #666; line-height: 1.8;">Standard shipping takes 5-7 business days. Express shipping (2-3 days) and overnight shipping are also available.</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">Do you ship internationally?</h3>
<p style="color: #666; line-height: 1.8;">Yes! We ship to over 50 countries worldwide. International orders typically arrive in 7-14 business days.</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">What is your return policy?</h3>
<p style="color: #666; line-height: 1.8;">We offer a 30-day return policy on most items. Items must be unused and in original packaging. See our <a href="/returns" style="color: #667eea;">Returns page</a> for details.</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">How can I track my order?</h3>
<p style="color: #666; line-height: 1.8;">Once your order ships, you'll receive a tracking number via email. You can also check your order status in your account dashboard.</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">Do you offer gift wrapping?</h3>
<p style="color: #666; line-height: 1.8;">Yes! Add gift wrapping to your order at checkout for just $4.99. We'll include a personalized message card.</p>
</div>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid #667eea; margin-top: 30px;">
<p style="color: #666; margin: 0;"><strong>Still have questions?</strong> Contact us at <a href="mailto:support@skyartshop.com" style="color: #667eea;">support@skyartshop.com</a></p>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core Scripts (standardized order) -->
<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 src="/assets/js/navigation.js"></script>
</body>
</html>

View File

@@ -1,265 +1,399 @@
<!DOCTYPE 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="Frequently Asked Questions - Sky Art Shop"
/>
<title>FAQ - Sky Art Shop</title>
<!-- Fonts -->
<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"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap"
rel="stylesheet"
/>
<!-- Icons -->
<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=1768447584" />
<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" />
<!-- Modern Theme CSS -->
<link rel="stylesheet" href="/assets/css/modern-theme.css?v=20260118drawer" />
<link rel="stylesheet" href="/assets/css/mobile-fixes.css?v=20260118c" />
<style>
.faq-container {
max-width: 900px;
margin: 0 auto;
}
.faq-item {
background: var(--bg-white);
border-radius: var(--radius-lg);
margin-bottom: var(--spacing-md);
overflow: hidden;
box-shadow: var(--shadow-sm);
transition: var(--transition-smooth);
}
.faq-item:hover {
box-shadow: var(--shadow-md);
}
.faq-question {
padding: var(--spacing-lg);
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
color: var(--text-primary);
transition: var(--transition-fast);
}
.faq-question:hover {
background: var(--primary-pink-light);
}
.faq-icon {
font-size: 1.2rem;
transition: var(--transition-fast);
color: var(--primary-pink-dark);
}
.faq-item.active .faq-icon {
transform: rotate(180deg);
}
.faq-answer {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.faq-answer-content {
padding: 0 var(--spacing-lg) var(--spacing-lg);
color: var(--text-secondary);
line-height: 1.8;
}
.faq-item.active .faq-answer {
max-height: 500px;
}
.contact-box {
background: var(--primary-pink-light);
padding: var(--spacing-xl);
border-radius: var(--radius-lg);
text-align: center;
margin-top: var(--spacing-2xl);
}
.contact-box h3 {
margin-bottom: var(--spacing-md);
}
</style>
</head>
<body>
<script>window.__bodyReady=true</script>
<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>
<!-- Navigation -->
<header class="nav-wrapper">
<nav class="navbar">
<a href="/home" class="nav-brand">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop Logo"
/>
<span>Sky Art Shop</span>
</a>
<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 active">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 class="nav-menu">
<li><a href="/home" class="nav-link">Home</a></li>
<li><a href="/shop" class="nav-link">Shop</a></li>
<li><a href="/portfolio" class="nav-link">Portfolio</a></li>
<li><a href="/blog" class="nav-link">Blog</a></li>
<li><a href="/about" class="nav-link">About</a></li>
<li><a href="/contact" class="nav-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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1><i class="bi bi-question-circle"></i> Frequently Asked Questions</h1>
<p class="hero-subtitle">Quick answers to common questions</p>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<div class="container">
<div style="max-width: 800px; margin: 0 auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">How long does shipping take?</h3>
<p style="color: #666; line-height: 1.8;">Standard shipping takes 5-7 business days. Express shipping (2-3 days) and overnight shipping are also available.</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">Do you ship internationally?</h3>
<p style="color: #666; line-height: 1.8;">Yes! We ship to over 50 countries worldwide. International orders typically arrive in 7-14 business days.</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">What is your return policy?</h3>
<p style="color: #666; line-height: 1.8;">We offer a 30-day return policy on most items. Items must be unused and in original packaging. See our <a href="/returns" style="color: #667eea;">Returns page</a> for details.</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">How can I track my order?</h3>
<p style="color: #666; line-height: 1.8;">Once your order ships, you'll receive a tracking number via email. You can also check your order status in your account dashboard.</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea;">Do you offer gift wrapping?</h3>
<p style="color: #666; line-height: 1.8;">Yes! Add gift wrapping to your order at checkout for just $4.99. We'll include a personalized message card.</p>
</div>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid #667eea; margin-top: 30px;">
<p style="color: #666; margin: 0;"><strong>Still have questions?</strong> Contact us at <a href="mailto:support@skyartshop.com" style="color: #667eea;">support@skyartshop.com</a></p>
<div class="nav-actions">
<a href="/signin" class="nav-icon-btn" title="Sign In">
<i class="bi bi-person"></i>
</a>
<button class="nav-icon-btn wishlist-btn-nav" title="Wishlist">
<i class="bi bi-heart"></i>
<span class="badge wishlist-count" style="display: none">0</span>
</button>
<button class="nav-icon-btn cart-btn" title="Cart">
<i class="bi bi-bag"></i>
<span class="badge cart-count" style="display: none">0</span>
</button>
<button class="nav-mobile-toggle" aria-label="Toggle menu">
<span></span>
<span></span>
<span></span>
</button>
</div>
</nav>
</header>
<!-- Page Content -->
<main class="page-content">
<!-- Page Header -->
<div class="page-header">
<div class="container">
<h1 id="faqHeaderTitle">
<i class="bi bi-question-circle"></i> Frequently Asked Questions
</h1>
<p id="faqHeaderSubtitle">Quick answers to common questions</p>
<div class="breadcrumb">
<a href="/home">Home</a>
<span>/</span>
<span>FAQ</span>
</div>
</div>
</div>
</section>
<!-- FAQ Section -->
<section class="section">
<div class="container">
<div class="faq-container" id="faqContainer">
<!-- FAQ items will be loaded from API -->
<div class="loading-spinner" style="margin: 40px auto"></div>
</div>
<div class="contact-box">
<h3>Still have questions?</h3>
<p>Our customer service team is here to help!</p>
<div
style="
display: flex;
gap: var(--spacing-md);
justify-content: center;
margin-top: var(--spacing-lg);
flex-wrap: wrap;
"
>
<a href="/contact" class="btn btn-primary">Contact Us</a>
<a href="mailto:support@skyartshop.com" class="btn btn-outline"
>Email Support</a
>
</div>
</div>
</div>
</section>
</main>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">Sky Art Shop</h3>
<p class="footer-text">
Your destination for unique art pieces and creative supplies.
<div class="footer-about">
<div class="footer-brand">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop"
/>
<span>Sky Art Shop</span>
</div>
<p>
Your one-stop shop for scrapbooking, journaling, cardmaking, and
collaging stationery. Quality products for all your creative
needs.
</p>
<div class="social-links">
<div class="footer-social">
<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>
<a href="#" class="social-link"
><i class="bi bi-instagram"></i
></a>
<a href="#" class="social-link"
><i class="bi bi-pinterest"></i
></a>
<a href="#" class="social-link"><i class="bi bi-youtube"></i></a>
</div>
</div>
<div class="footer-col">
<h4 class="footer-heading">Shop</h4>
<div class="footer-column">
<h4>Quick Links</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="/home">Home</a></li>
<li><a href="/shop">Shop</a></li>
<li><a href="/portfolio">Portfolio</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/about">About Us</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<div class="footer-col">
<h4 class="footer-heading">Customer Service</h4>
<div class="footer-column">
<h4>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="/shipping-info">Shipping Info</a></li>
<li><a href="/returns">Returns & Refunds</a></li>
<li><a href="/privacy">Privacy Policy</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Contact Us</h4>
<ul class="footer-links">
<li><i class="bi bi-envelope"></i> hello@skyartshop.com</li>
<li><i class="bi bi-telephone"></i> (555) 123-4567</li>
<li><i class="bi bi-geo-alt"></i> 123 Creative Lane</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 Sky Art Shop. All rights reserved.</p>
<p>&copy; 2026 Sky Art Shop. All rights reserved.</p>
<p>
Made with
<i class="bi bi-heart-fill" style="color: var(--primary-pink)"></i>
for creative souls
</p>
</div>
</div>
</footer>
<!-- Core Scripts (standardized order) -->
<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 src="/assets/js/navigation.js"></script>
<!-- Cart Drawer -->
<div class="cart-overlay"></div>
<div class="cart-drawer">
<div class="cart-header">
<h3>Shopping Cart</h3>
<button class="cart-close"><i class="bi bi-x"></i></button>
</div>
<div class="cart-items">
<!-- Cart items rendered via JavaScript -->
</div>
<div class="cart-footer">
<div class="cart-total">
<span>Total:</span>
<span class="cart-total-amount">$0.00</span>
</div>
<a href="/checkout" class="btn btn-primary cart-checkout-btn"
>Checkout</a
>
</div>
</div>
<!-- Wishlist Drawer -->
<div class="wishlist-overlay"></div>
<div class="wishlist-drawer">
<div class="wishlist-header">
<h3><i class="bi bi-heart"></i> My Wishlist</h3>
<button class="wishlist-close"><i class="bi bi-x"></i></button>
</div>
<div class="wishlist-items"></div>
<div class="wishlist-footer">
<a href="/shop" class="btn btn-outline">Continue Shopping</a>
</div>
</div>
<!-- Scripts -->
<script src="/assets/js/modern-theme.js?v=20260118c"></script>
<script src="/assets/js/customer-auth.js"></script>
<script>
// Load FAQ page data from API
async function loadFaqPageData() {
const container = document.getElementById("faqContainer");
try {
const response = await fetch("/api/pages/faq");
const data = await response.json();
if (data.success && data.page && data.page.pagedata) {
const pagedata =
typeof data.page.pagedata === "string"
? JSON.parse(data.page.pagedata)
: data.page.pagedata;
// Update header
if (pagedata.header) {
if (pagedata.header.title) {
document.getElementById("faqHeaderTitle").innerHTML =
'<i class="bi bi-question-circle"></i> ' +
pagedata.header.title;
}
if (pagedata.header.subtitle) {
document.getElementById("faqHeaderSubtitle").textContent =
pagedata.header.subtitle;
}
}
// Render FAQ items
if (pagedata.items && pagedata.items.length > 0) {
container.innerHTML = pagedata.items
.map(
(item, index) => `
<div class="faq-item ${index === 0 ? "active" : ""}">
<div class="faq-question">
<span>${escapeHtml(item.question)}</span>
<i class="bi bi-chevron-down faq-icon"></i>
</div>
<div class="faq-answer" ${index === 0 ? 'style="max-height: 500px;"' : ""}>
<div class="faq-answer-content">
<p>${item.answer}</p>
</div>
</div>
</div>
`,
)
.join("");
// Setup accordion behavior
setupFaqAccordion();
} else {
container.innerHTML =
'<p class="text-center">No FAQs available yet.</p>';
}
} else {
container.innerHTML =
'<p class="text-center">Unable to load FAQs.</p>';
}
} catch (error) {
console.error("Error loading FAQ page:", error);
container.innerHTML =
'<p class="text-center">Unable to load FAQs.</p>';
}
}
function setupFaqAccordion() {
document.querySelectorAll(".faq-item").forEach((item) => {
const question = item.querySelector(".faq-question");
question.addEventListener("click", () => {
const isActive = item.classList.contains("active");
// Close all items
document.querySelectorAll(".faq-item").forEach((i) => {
i.classList.remove("active");
i.querySelector(".faq-answer").style.maxHeight = "0";
});
// Open clicked item if it wasn't active
if (!isActive) {
item.classList.add("active");
item.querySelector(".faq-answer").style.maxHeight = "500px";
}
});
});
}
function escapeHtml(text) {
if (!text) return "";
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
// Load page data on DOMContentLoaded
document.addEventListener("DOMContentLoaded", loadFaqPageData);
</script>
<script src="/assets/js/accessibility.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html>
<head>
<title>Cat Logo Preview</title>
<style>
body {
font-family: "Inter", sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
padding: 20px;
}
.container {
background: white;
padding: 40px;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
text-align: center;
max-width: 600px;
}
h1 {
color: #2d3436;
margin-bottom: 10px;
}
p {
color: #636e72;
margin-bottom: 30px;
}
.logo-display {
display: flex;
gap: 40px;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 30px 0;
}
.logo-box {
padding: 30px;
background: #f8f9fa;
border-radius: 15px;
transition: transform 0.3s ease;
}
.logo-box:hover {
transform: scale(1.05);
}
.logo-box img {
display: block;
}
.size-label {
margin-top: 15px;
font-size: 14px;
color: #636e72;
font-weight: 500;
}
.info {
background: #fec6df;
padding: 20px;
border-radius: 10px;
margin-top: 30px;
}
.info h3 {
color: #c239b3;
margin-top: 0;
}
.info code {
background: white;
padding: 2px 8px;
border-radius: 4px;
color: #c239b3;
font-family: "Courier New", monospace;
}
</style>
</head>
<body>
<div class="container">
<h1>🐱 Sky Art Shop Cat Logo</h1>
<p>Your creative companion logo is ready!</p>
<div class="logo-display">
<div class="logo-box">
<img
src="/assets/images/logo/cat-logo.svg"
width="100"
height="100"
alt="Cat Logo - Large"
/>
<div class="size-label">Large (100px)</div>
</div>
<div class="logo-box">
<img
src="/assets/images/logo/cat-logo.svg"
width="60"
height="60"
alt="Cat Logo - Medium"
/>
<div class="size-label">Medium (60px)</div>
</div>
<div class="logo-box">
<img
src="/assets/images/logo/cat-logo.svg"
width="40"
height="40"
alt="Cat Logo - Small"
/>
<div class="size-label">Small (40px)</div>
</div>
</div>
<div class="info">
<h3>✨ Logo Applied to About Page</h3>
<p style="color: #2d3436; margin: 10px 0">
The cat logo now appears next to "Our Story" section on the About
page!
</p>
<p style="color: #636e72; font-size: 14px; margin: 5px 0">
Location: <code>/assets/images/logo/cat-logo.svg</code>
</p>
<p style="color: #636e72; font-size: 14px; margin: 5px 0">
<strong>View it:</strong>
<a href="/about" style="color: #c239b3">Go to About Page →</a>
</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,434 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Security Headers -->
<title>About - Sky Art Shop</title>
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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" />
</head>
<body>
<script>
window.__bodyReady = true;
</script>
<!-- Modern Navigation -->
<div class="sticky-banner-wrapper">
<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 active">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">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>
</nav>
</div>
<section class="about-hero">
<div class="container">
<h1>About Sky Art Shop</h1>
<p class="hero-subtitle">Your creative journey starts here</p>
</div>
</section>
<section class="about-content">
<div class="container">
<div class="about-layout">
<div class="about-main-content">
<div class="about-text" id="aboutContent">
<div style="text-align: center; padding: 40px">
<div
class="loading-spinner"
style="
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
"
></div>
<p>Loading content...</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Team Members Section -->
<section class="team-section" id="teamSection" style="display: none">
<div class="container">
<div class="team-header">
<h2 class="section-title">Meet Our Team</h2>
<p class="section-subtitle">
The talented people behind Sky Art Shop
</p>
</div>
<div class="team-grid" id="teamMembersGrid">
<div style="text-align: center; padding: 40px">
<div
class="loading-spinner"
style="
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
"
></div>
<p>Loading team...</p>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core utilities and state management -->
<script src="/assets/js/api-cache.js"></script>
<script src="/assets/js/main.js"></script>
<!-- Shopping system (handles cart & wishlist) -->
<script src="/assets/js/shop-system.js"></script>
<!-- Page interactions -->
<script src="/assets/js/page-transitions.js?v=1766709739"></script>
<script src="/assets/js/back-button-control.js?v=1766723554"></script>
<script src="/assets/js/navigation.js"></script>
<!-- Page-specific content loading -->
<script>
// Load about page content from API
async function loadAboutContent() {
try {
const response = await fetch("/api/pages/about");
const data = await response.json();
if (data.success && data.page) {
const contentDiv = document.getElementById("aboutContent");
// Check if content is Quill Delta format (JSON)
if (data.page.content) {
try {
const delta = JSON.parse(data.page.content);
// Convert Delta to HTML
contentDiv.innerHTML = convertDeltaToHTML(delta);
} catch {
// If not JSON, treat as plain HTML
contentDiv.innerHTML = data.page.content;
}
} else {
contentDiv.innerHTML = "<p>Content not available.</p>";
}
// Update meta tags if available
if (data.page.metatitle) {
document.title = data.page.metatitle;
}
if (data.page.metadescription) {
const metaDesc = document.querySelector(
'meta[name="description"]'
);
if (metaDesc) {
metaDesc.content = data.page.metadescription;
}
}
} else {
document.getElementById("aboutContent").innerHTML =
"<p>Unable to load content.</p>";
}
} catch (error) {
console.error("Error loading about content:", error);
document.getElementById("aboutContent").innerHTML =
"<p>Error loading content.</p>";
}
}
// Convert Quill Delta to HTML
function convertDeltaToHTML(delta) {
if (!delta || !delta.ops) return "";
let html = "";
let currentBlock = "";
delta.ops.forEach((op) => {
if (typeof op.insert === "string") {
let text = op.insert;
// Apply text formatting
if (op.attributes) {
if (op.attributes.bold) text = `<strong>${text}</strong>`;
if (op.attributes.italic) text = `<em>${text}</em>`;
if (op.attributes.underline) text = `<u>${text}</u>`;
if (op.attributes.strike) text = `<s>${text}</s>`;
if (op.attributes.code) text = `<code>${text}</code>`;
if (op.attributes.link)
text = `<a href="${op.attributes.link}" target="_blank">${text}</a>`;
if (op.attributes.color)
text = `<span style="color: ${op.attributes.color}">${text}</span>`;
if (op.attributes.background)
text = `<span style="background-color: ${op.attributes.background}">${text}</span>`;
}
// Handle line breaks
const lines = text.split("\n");
lines.forEach((line, index) => {
currentBlock += line;
if (index < lines.length - 1) {
// New paragraph
if (currentBlock.trim()) {
html += `<p>${currentBlock}</p>`;
}
currentBlock = "";
}
});
}
});
// Add remaining content
if (currentBlock.trim()) {
html += `<p>${currentBlock}</p>`;
}
return html || "<p>Content not available.</p>";
}
// Load team members
async function loadTeamMembers() {
try {
const response = await fetch("/api/team-members");
const data = await response.json();
if (data.success && data.teamMembers && data.teamMembers.length > 0) {
displayTeamMembers(data.teamMembers);
document.getElementById("teamSection").style.display = "block";
}
} catch (error) {
console.error("Error loading team members:", error);
}
}
// Display team members
function displayTeamMembers(members) {
const grid = document.getElementById("teamMembersGrid");
grid.innerHTML = members
.map(
(member) => `
<div class="team-card">
<div class="team-image-wrapper">
<div class="team-image">
${
member.image_url
? `<img src="${member.image_url}" alt="${escapeHtml(
member.name
)}" />`
: `<i class="bi bi-person-circle"></i>`
}
</div>
</div>
<h3 class="team-name">${escapeHtml(member.name)}</h3>
<div class="team-position">${escapeHtml(member.position)}</div>
${
member.bio
? `<p class="team-bio">${escapeHtml(member.bio)}</p>`
: ""
}
</div>
`
)
.join("");
}
// Escape HTML to prevent XSS
function escapeHtml(text) {
if (!text) return "";
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
// Load content when page loads
document.addEventListener("DOMContentLoaded", function () {
loadAboutContent();
loadTeamMembers();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,329 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Security Headers -->
<title>About - Sky Art Shop</title>
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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" />
</head>
<body>
<script>window.__bodyReady=true</script>
<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 active">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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1>Blog</h1>
<p class="hero-subtitle">Inspiration, tips, and creative ideas</p>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<div class="container">
<div id="loadingMessage" style="text-align: center; padding: 40px">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p style="margin-top: 15px; color: #666">Loading blog posts...</p>
</div>
<div
id="blogGrid"
style="
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 30px;
"
></div>
<div
id="noPosts"
style="display: none; text-align: center; padding: 40px; color: #666"
>
<i
class="bi bi-journal-text"
style="font-size: 48px; color: #ccc; margin-bottom: 15px"
></i>
<p>No blog posts available at the moment.</p>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core dependencies (load in order) -->
<script src="/assets/js/api-cache.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/shop-system.js"></script>
<!-- UI enhancements -->
<script src="/assets/js/page-transitions.js?v=1766709739"></script>
<script src="/assets/js/back-button-control.js?v=1766723554"></script>
<script src="/assets/js/navigation.js"></script>
<!-- Page-specific functionality -->
<script>
// Load blog posts from API
async function loadBlog() {
try {
const response = await window.apiCache.fetch("/api/blog/posts");
if (response.ok) {
const data = await response.json();
const posts = data.posts || [];
document.getElementById("loadingMessage").style.display = "none";
if (posts.length === 0) {
document.getElementById("noPosts").style.display = "block";
return;
}
const grid = document.getElementById("blogGrid");
grid.innerHTML = posts
.map(
(post) => `
<article class="blog-card" style="background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); transition: transform 0.3s; cursor: pointer;" onclick="window.location.href='/blog/${
post.slug
}'">
${
post.imageurl
? `
<div class="blog-image" style="position: relative; padding-top: 56.25%; overflow: hidden; background: #f5f5f5;">
<img src="${post.imageurl}"
alt="${post.title}"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover;"
loading="lazy" />
</div>
`
: ""
}
<div style="padding: 25px;">
<div style="display: flex; align-items: center; gap: 15px; margin-bottom: 12px; color: #999; font-size: 13px;">
<span><i class="bi bi-calendar"></i> ${post.createdat && Object.keys(post.createdat).length > 0 ? new Date(post.createdat).toLocaleDateString() : 'Recent'}</span>
</div>
<h2 style="font-size: 22px; font-weight: 600; margin-bottom: 12px; color: #333; line-height: 1.3;">${
post.title
}</h2>
<p style="color: #666; font-size: 15px; line-height: 1.6; margin-bottom: 20px;">${
post.excerpt ||
post.content?.substring(0, 150) + "..." ||
""
}</p>
<a href="/blog/${
post.slug
}" style="display: inline-flex; align-items: center; color: #667eea; font-weight: 500; text-decoration: none; transition: gap 0.3s;" onclick="event.stopPropagation()">
Read More <i class="bi bi-arrow-right" style="margin-left: 8px;"></i>
</a>
</article>
`
)
.join("");
} else {
document.getElementById("noPosts").style.display = "block";
}
} catch (error) {
console.error("Error loading blog:", error);
document.getElementById("loadingMessage").innerHTML =
'<p style="color: #dc3545;">Error loading blog posts. Please try again later.</p>';
}
}
// Initialize
loadBlog();
</script>
</body>
</html>

View File

@@ -0,0 +1,642 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Security Headers -->
<title>Contact Us - Sky Art Shop</title>
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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/theme-colors.css" />
</head>
<body>
<script>
window.__bodyReady = true;
</script>
<!-- Modern Navigation -->
<div class="sticky-banner-wrapper">
<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 active">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">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>
</nav>
</div>
<!-- Contact Hero -->
<section
class="contact-hero"
style="
background: linear-gradient(135deg, #f6ccde 0%, #fcb1d8 100%);
padding: 40px 0 30px;
color: #202023;
text-align: center;
"
>
<div class="container">
<h1
style="
font-size: 2.5rem;
margin-bottom: 16px;
font-weight: 700;
color: #202023;
"
>
Get In Touch
</h1>
<p
style="
font-size: 1.1rem;
color: #202023;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
"
>
Have questions or feedback? We'd love to hear from you. Send us a
message and we'll respond as soon as possible.
</p>
</div>
</section>
<!-- Business Contact Information -->
<section
style="padding: 60px 0 40px; background: white"
id="contactInfoSection"
>
<div class="container" style="max-width: 1000px">
<div style="text-align: center; padding: 40px">
<div
class="loading-spinner"
style="
border: 4px solid #f3f3f3;
border-top: 4px solid #fcb1d8;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
"
></div>
<p>Loading contact information...</p>
</div>
</div>
</section>
<!-- Contact Form Section -->
<section
class="contact-section"
style="padding: 80px 0; background: #f8f9fa"
>
<div class="container" style="max-width: 800px">
<div style="text-align: center; margin-bottom: 40px">
<h2
style="
font-size: 2rem;
font-weight: 700;
color: #202023;
margin-bottom: 12px;
"
>
Send Us a Message
</h2>
<p style="font-size: 1rem; color: #202023">
Fill out the form below and we'll get back to you within 24 hours
</p>
</div>
<div
class="contact-form-wrapper"
style="
background: white;
border-radius: 16px;
padding: 48px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
"
>
<form
id="contactForm"
style="display: flex; flex-direction: column; gap: 16px"
>
<!-- Name and Email Row -->
<div
style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px"
>
<!-- Name Field -->
<div class="form-group">
<label
for="name"
style="
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #2d3436;
font-size: 15px;
"
>
Full Name <span style="color: #ff6b6b">*</span>
</label>
<input
type="text"
id="name"
name="name"
required
placeholder="John Doe"
style="
width: 100%;
padding: 14px 16px;
border: 2px solid #e1e8ed;
border-radius: 8px;
font-size: 15px;
transition: all 0.3s;
font-family: 'Roboto', sans-serif;
"
onfocus="this.style.borderColor='#FCB1D8'; this.style.outline='none';"
onblur="this.style.borderColor='#e1e8ed';"
/>
</div>
<!-- Email Field -->
<div class="form-group">
<label
for="email"
style="
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #202023;
font-size: 15px;
"
>
Email Address <span style="color: #fcb1d8">*</span>
</label>
<input
type="email"
id="email"
name="email"
required
placeholder="john@example.com"
style="
width: 100%;
padding: 14px 16px;
border: 2px solid #e1e8ed;
border-radius: 8px;
font-size: 15px;
transition: all 0.3s;
font-family: 'Roboto', sans-serif;
"
onfocus="this.style.borderColor='#FCB1D8'; this.style.outline='none';"
onblur="this.style.borderColor='#e1e8ed';"
/>
</div>
</div>
<!-- Phone and Subject Row -->
<div
style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px"
>
<!-- Phone Field -->
<div class="form-group">
<label
for="phone"
style="
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #202023;
font-size: 15px;
"
>
Phone Number
<span style="color: #202023; font-weight: 400; opacity: 0.6"
>(Optional)</span
>
</label>
<input
type="tel"
id="phone"
name="phone"
placeholder="(123) 456-7890"
style="
width: 100%;
padding: 14px 16px;
border: 2px solid #e1e8ed;
border-radius: 8px;
font-size: 15px;
transition: all 0.3s;
font-family: 'Roboto', sans-serif;
"
onfocus="this.style.borderColor='#FCB1D8'; this.style.outline='none';"
onblur="this.style.borderColor='#e1e8ed';"
/>
</div>
<!-- Subject Field -->
<div class="form-group">
<label
for="subject"
style="
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #2d3436;
font-size: 15px;
"
>
Subject <span style="color: #ff6b6b">*</span>
</label>
<input
type="text"
id="subject"
name="subject"
required
placeholder="How can we help you?"
style="
width: 100%;
padding: 14px 16px;
border: 2px solid #e1e8ed;
border-radius: 8px;
font-size: 15px;
transition: all 0.3s;
font-family: 'Roboto', sans-serif;
"
onfocus="this.style.borderColor='#FCB1D8'; this.style.outline='none';"
onblur="this.style.borderColor='#e1e8ed';"
/>
</div>
</div>
<!-- Message Field -->
<div class="form-group">
<label
for="message"
style="
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #202023;
font-size: 15px;
"
>
Full Name <span style="color: #fcb1d8">*</span>
</label>
<textarea
id="message"
name="message"
required
rows="6"
placeholder="Tell us more about your inquiry..."
style="
width: 100%;
padding: 14px 16px;
border: 2px solid #e1e8ed;
border-radius: 8px;
font-size: 15px;
transition: all 0.3s;
resize: vertical;
font-family: 'Roboto', sans-serif;
line-height: 1.6;
"
onfocus="this.style.borderColor='#FCB1D8'; this.style.outline='none';"
onblur="this.style.borderColor='#e1e8ed';"
></textarea>
</div>
<!-- Submit Button -->
<div style="margin-top: 8px">
<button
type="submit"
style="
width: 100%;
padding: 16px 32px;
background: linear-gradient(135deg, #f6ccde 0%, #fcb1d8 100%);
color: #202023;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
font-family: 'Roboto', sans-serif;
"
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 8px 20px rgba(252,177,216,0.4)';"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';"
>
<i class="bi bi-send" style="margin-right: 8px"></i>
Send Message
</button>
</div>
<!-- Success/Error Message -->
<div
id="formMessage"
style="
display: none;
padding: 16px;
border-radius: 8px;
text-align: center;
font-weight: 500;
"
></div>
</form>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core utilities and state management -->
<script src="/assets/js/api-cache.js"></script>
<script src="/assets/js/main.js"></script>
<!-- Shopping system (handles cart & wishlist) -->
<script src="/assets/js/shop-system.js"></script>
<!-- Page interactions -->
<script src="/assets/js/page-transitions.js?v=1766709739"></script>
<script src="/assets/js/back-button-control.js?v=1766723554"></script>
<script src="/assets/js/navigation.js"></script>
<!-- Page-specific functionality -->
<script>
// Handle contact form submission
document
.getElementById("contactForm")
.addEventListener("submit", async function (e) {
e.preventDefault();
const formMessage = document.getElementById("formMessage");
const submitButton = e.target.querySelector('button[type="submit"]');
const originalButtonText = submitButton.innerHTML;
// Disable button and show loading state
submitButton.disabled = true;
submitButton.innerHTML =
'<i class="bi bi-hourglass-split" style="margin-right: 8px;"></i>Sending...';
// Get form data
const formData = {
name: document.getElementById("name").value,
email: document.getElementById("email").value,
phone: document.getElementById("phone").value,
subject: document.getElementById("subject").value,
message: document.getElementById("message").value,
timestamp: new Date().toISOString(),
};
try {
// Simulate API call (replace with actual endpoint when ready)
await new Promise((resolve) => setTimeout(resolve, 1500));
// Show success message
formMessage.style.display = "block";
formMessage.style.background = "#d4edda";
formMessage.style.color = "#155724";
formMessage.style.border = "1px solid #c3e6cb";
formMessage.innerHTML =
'<i class="bi bi-check-circle" style="margin-right: 8px;"></i>Thank you! Your message has been sent successfully. We\'ll get back to you soon.';
// Reset form
e.target.reset();
// Log to console (for now)
console.log("Contact form submitted:", formData);
} catch (error) {
// Show error message
formMessage.style.display = "block";
formMessage.style.background = "#f8d7da";
formMessage.style.color = "#721c24";
formMessage.style.border = "1px solid #f5c6cb";
formMessage.innerHTML =
'<i class="bi bi-exclamation-circle" style="margin-right: 8px;"></i>Oops! Something went wrong. Please try again or email us directly.';
console.error("Contact form error:", error);
} finally {
// Re-enable button
submitButton.disabled = false;
submitButton.innerHTML = originalButtonText;
// Hide message after 5 seconds
setTimeout(() => {
formMessage.style.display = "none";
}, 5000);
}
});
// Load contact information from API
async function loadContactInfo() {
try {
const response = await fetch("/api/pages/contact");
const data = await response.json();
if (data.success && data.page) {
const section = document.getElementById("contactInfoSection");
section.innerHTML = `
<div class="container" style="max-width: 1000px">
${data.page.content}
</div>
`;
// Update meta tags
if (data.page.metatitle) {
document.title = data.page.metatitle;
}
if (data.page.metadescription) {
const metaDesc = document.querySelector(
'meta[name="description"]'
);
if (metaDesc) {
metaDesc.content = data.page.metadescription;
}
}
}
} catch (error) {
console.error("Error loading contact info:", error);
document.getElementById("contactInfoSection").innerHTML =
'<div class="container"><p style="text-align:center;">Error loading contact information.</p></div>';
}
}
// Load content when page loads
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", loadContactInfo);
} else {
loadContactInfo();
}
</script>
</body>
</html>

View File

@@ -0,0 +1,625 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Security Headers -->
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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" />
<style>
/* Body Reset */
body {
margin: 0;
padding: 0;
background-color: #ffebeb;
min-height: 100vh;
}
/* Sticky Banner Container */
.sticky-banner-wrapper {
position: sticky;
top: 0;
z-index: 1000;
background: #ffd0d0;
}
/* Override navbar position when inside sticky wrapper */
.sticky-banner-wrapper .modern-navbar {
position: relative;
box-shadow: none;
}
</style>
</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>

View File

@@ -0,0 +1,482 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Security Headers -->
<title>About - Sky Art Shop</title>
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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" />
</head>
<body>
<script>window.__bodyReady=true</script>
<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 active">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>
<section class="about-hero">
<div class="container">
<h1>Portfolio</h1>
<p class="hero-subtitle">Explore our creative projects and artwork</p>
</section>
<section
class="portfolio-section"
style="padding: 60px 0; background: #ffebeb"
>
<div class="container">
<div id="loadingMessage" style="text-align: center; padding: 40px">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p style="margin-top: 15px; color: #666">
Loading portfolio projects...
</p>
</div>
<div
id="portfolioGrid"
class="products-grid"
style="
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 30px;
"
></div>
<div
id="noProjects"
style="display: none; text-align: center; padding: 40px; color: #666"
>
<i
class="bi bi-images"
style="font-size: 48px; color: #ccc; margin-bottom: 15px"
></i>
<p>No portfolio projects available at the moment.</p>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Project Modal -->
<div
id="projectModal"
style="
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
z-index: 9999;
overflow-y: auto;
overflow-x: hidden;
padding: 40px 0;
"
onclick="if(event.target.id === 'projectModal') closeProjectModal();"
>
<div
style="
position: relative;
margin: auto;
width: 90%;
max-width: 900px;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
"
onclick="event.stopPropagation();"
>
<button
onclick="closeProjectModal()"
style="
position: absolute;
top: 20px;
right: 20px;
background: white;
border: none;
width: 44px;
height: 44px;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transition: all 0.2s;
"
onmouseover="this.style.transform='scale(1.1)'; this.style.background='#f8f9fa';"
onmouseout="this.style.transform='scale(1)'; this.style.background='white';"
>
<i class="bi bi-x-lg"></i>
</button>
<div
id="modalContent"
></div>
</div>
</div>
<!-- Core dependencies (load in order) -->
<script src="/assets/js/api-cache.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/shop-system.js"></script>
<!-- UI enhancements -->
<script src="/assets/js/page-transitions.js?v=1766709739"></script>
<script src="/assets/js/back-button-control.js?v=1766723554"></script>
<script src="/assets/js/navigation.js"></script>
<!-- Page-specific functionality -->
<script>
let portfolioProjects = [];
// Open project modal
function openProjectModal(projectId) {
const project = portfolioProjects.find((p) => p.id === projectId);
if (!project) {
console.error('[Portfolio] Project not found:', projectId);
return;
}
if (!project.title) {
console.error('[Portfolio] Invalid project data - missing title:', project);
return;
}
const modal = document.getElementById("projectModal");
const modalContent = document.getElementById("modalContent");
if (!modal || !modalContent) {
console.error('[Portfolio] Modal elements not found');
return;
}
// Build category badge HTML
const categoryBadge = project.category
? `<span style="display: inline-block; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 8px 18px; border-radius: 24px; font-size: 13px; font-weight: 600; margin-bottom: 24px; letter-spacing: 0.5px; text-transform: uppercase;">${project.category}</span>`
: '';
// Build modal content
modalContent.innerHTML = `
<div class="project-image" style="width: 100%; height: 450px; overflow: hidden; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); flex-shrink: 0;">
<img src="${project.featuredimage || project.imageurl || '/assets/images/placeholder.svg'}"
alt="${project.title}"
onerror="this.src='/assets/images/placeholder.svg'"
style="width: 100%; height: 100%; object-fit: cover;" />
</div>
<div style="padding: 40px; background: white;">
${categoryBadge}
<h2 style="font-size: 36px; font-weight: 700; margin: 0 0 24px 0; color: #1a1a1a; line-height: 1.2;">${project.title}</h2>
<div style="color: #555; font-size: 17px; line-height: 1.9; margin-bottom: 32px; font-weight: 400;">
${project.description || 'No description available.'}
</div>
</div>
`;
modal.style.display = "block";
document.body.style.overflow = "hidden";
document.documentElement.style.overflow = "hidden";
}
// Close project modal
function closeProjectModal() {
document.getElementById("projectModal").style.display = "none";
document.body.style.overflow = "auto";
document.documentElement.style.overflow = "auto";
}
// Close modal on outside click
document.addEventListener("click", (e) => {
const modal = document.getElementById("projectModal");
if (e.target === modal) {
closeProjectModal();
}
});
// Close modal on Escape key
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
closeProjectModal();
}
});
// Load portfolio projects from API
async function loadPortfolio() {
try {
const response = await window.apiCache.fetch("/api/portfolio/projects");
if (response.ok) {
const data = await response.json();
portfolioProjects = data.projects || [];
document.getElementById("loadingMessage").style.display = "none";
if (portfolioProjects.length === 0) {
document.getElementById("noProjects").style.display = "block";
return;
}
// Validate and filter projects
const validProjects = portfolioProjects.filter(project => {
if (!project || !project.id || !project.title) {
console.warn('[Portfolio] Skipping invalid project:', project);
return false;
}
return true;
});
if (validProjects.length === 0) {
document.getElementById("noProjects").style.display = "block";
return;
}
const grid = document.getElementById("portfolioGrid");
grid.innerHTML = validProjects
.map(
(project) => `
<div class="product-card" onclick="openProjectModal('${
project.id
}')" style="background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); transition: all 0.3s; cursor: pointer;">
<div class="product-image" style="position: relative; padding-top: 100%; overflow: hidden; background: #f5f5f5;">
<img src="${
project.featuredimage || project.imageurl || "/assets/images/placeholder.svg"
}"
alt="${project.title}"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s;"
loading="lazy"
onerror="this.src='/assets/images/placeholder.svg'"
onmouseover="this.style.transform='scale(1.05)'"
onmouseout="this.style.transform='scale(1)'" />
${
project.category
? `<span style="position: absolute; top: 10px; right: 10px; background: rgba(102, 126, 234, 0.9); color: white; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 500;">${project.category}</span>`
: ""
}
</div>
<div style="padding: 20px; text-align: center;">
<h3 style="font-size: 18px; font-weight: 600; margin: 0; color: #333;">${
project.title
}</h3>
${
project.description && project.description.replace(/<[^>]*>/g, '').trim()
? `<p style="font-size: 14px; color: #666; margin: 8px 0 0 0; line-height: 1.5;">${project.description.replace(/<[^>]*>/g, '').substring(0, 80)}${project.description.length > 80 ? '...' : ''}</p>`
: ""
}
</div>
</div>
`
)
.join("");
} else {
document.getElementById("noProjects").style.display = "block";
}
} catch (error) {
console.error("Error loading portfolio:", error);
document.getElementById("loadingMessage").innerHTML =
'<p style="color: #dc3545;">Error loading portfolio projects. Please try again later.</p>';
}
}
// Initialize
loadPortfolio();
</script>
</body>
</html>

View File

@@ -0,0 +1,853 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Security Headers -->
<title>Product Details - 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/main.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<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/theme-colors.css" />
</head>
<body>
<!-- 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">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>
</nav>
<div
id="loading"
style="
text-align: center;
padding: 100px 20px;
font-size: 18px;
color: #202023;
background: #ffebeb;
"
>
<i
class="bi bi-hourglass-split"
style="font-size: 48px; display: block; margin-bottom: 20px"
></i>
Loading product...
</div>
<div id="productDetail" style="display: none"></div>
<!-- Core Scripts (load in dependency order) -->
<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/api-client.js"></script>
<script src="/assets/js/notifications.js"></script>
<script src="/assets/js/page-transitions.js?v=1766709739"></script>
<script src="/assets/js/back-button-control.js?v=1766723554"></script>
<script src="/assets/js/navigation.js?v=1766708114"></script>
<script>
// Function to change primary image
function changePrimaryImage(imageUrl, index) {
const primaryImg = document.getElementById("primaryImage");
if (primaryImg) {
primaryImg.src = imageUrl;
}
// Update thumbnail borders
const thumbnails = document.querySelectorAll(".thumbnail-item");
thumbnails.forEach((thumb, idx) => {
if (idx === index) {
thumb.style.border = "2px solid #6b46c1";
} else {
thumb.style.border = "1px solid #e5e7eb";
}
});
// Update color variant borders
const variants = document.querySelectorAll(".color-variant-circle");
variants.forEach((variant) => {
const onclick = variant.getAttribute("onclick");
if (onclick && onclick.includes(imageUrl)) {
variant.style.border = "3px solid #6b46c1";
} else {
variant.style.border = "3px solid #e5e7eb";
}
});
}
async function loadProduct() {
const params = new URLSearchParams(window.location.search);
const productId = params.get("id");
console.log("Product page loaded. URL:", window.location.href);
console.log("Product ID from URL:", productId);
if (!productId) {
console.error("No product ID in URL");
document.getElementById("loading").innerHTML =
'<p style="text-align: center; padding: 40px; color: #ef4444; font-size: 18px;">Product not found - No product ID in URL</p><div style="text-align: center;"><a href="/shop" style="color: #FCB1D8; text-decoration: none; font-weight: 600;">← Back to Shop</a></div>';
return;
}
try {
console.log(
"Fetching product from API:",
`/api/products/${productId}`
);
const response = await window.apiCache.fetch(
`/api/products/${productId}`
);
const data = await response.json();
console.log("API response:", data);
if (!data.success || !data.product) {
throw new Error("Product not found");
}
const product = data.product;
document.title = `${product.name} - Sky Art Shop`;
// Get primary image or first image from images array
let primaryImage = "/assets/images/placeholder.svg";
let imageGallery = [];
if (
product.images &&
Array.isArray(product.images) &&
product.images.length > 0
) {
// Find primary image
const primary = product.images.find((img) => img.is_primary);
if (primary) {
primaryImage = primary.image_url;
} else {
primaryImage = product.images[0].image_url;
}
imageGallery = product.images;
}
// Build image gallery HTML
let galleryHTML = "";
if (imageGallery.length > 0) {
galleryHTML = `
<div style="display: flex; gap: 12px; margin-top: 16px; overflow-x: auto; padding: 8px 0;">
${imageGallery
.map(
(img, idx) => `
<img src="${img.image_url}"
alt="${img.alt_text || product.name}"
onclick="changePrimaryImage('${img.image_url}')"
style="width: 80px; height: 80px; object-fit: cover; border-radius: 8px; cursor: pointer; border: ${
img.image_url === primaryImage
? "3px solid #6b46c1"
: "1px solid #e5e7eb"
};"
onerror="this.src='/assets/images/placeholder.svg'" />
`
)
.join("")}
</div>
`;
}
// Build product details HTML
let detailsHTML = "";
if (
product.sku ||
product.weight ||
product.dimensions ||
product.material
) {
detailsHTML = `
<div style="margin-bottom: 24px; padding: 20px; background: #FFD0D0; border-radius: 8px; border: 1px solid #FCB1D8;">
<h3 style="font-size: 16px; font-weight: 600; color: #202023; margin-bottom: 16px;">Product Details</h3>
${
product.sku
? `
<p style="margin-bottom: 8px; color: #202023; opacity: 0.8;">
<span style="font-weight: 500;">SKU:</span> ${product.sku}
</p>
`
: ""
}
${
product.weight
? `
<p style="margin-bottom: 8px; color: #202023; opacity: 0.8;">
<span style="font-weight: 500;">Weight:</span> ${product.weight}
</p>
`
: ""
}
${
product.dimensions
? `
<p style="margin-bottom: 8px; color: #202023; opacity: 0.8;">
<span style="font-weight: 500;">Dimensions:</span> ${product.dimensions}
</p>
`
: ""
}
${
product.material
? `
<p style="margin-bottom: 8px; color: #6b7280;">
<span style="font-weight: 500;">Material:</span> ${product.material}
</p>
`
: ""
}
</div>
`;
}
// Build badges HTML
let badgesHTML = "";
if (product.isfeatured || product.isbestseller) {
badgesHTML = `
<div style="display: flex; gap: 8px; margin-bottom: 16px;">
${
product.isfeatured
? `
<span style="display: inline-block; padding: 6px 12px; background: #FCB1D8; color: #202023; border-radius: 6px; font-size: 12px; font-weight: 600; box-shadow: 0 2px 4px rgba(252, 177, 216, 0.4);">
<i class="bi bi-star-fill"></i> Featured
</span>
`
: ""
}
${
product.isbestseller
? `
<span style="display: inline-block; padding: 6px 12px; background: #F6CCDE; color: #202023; border-radius: 6px; font-size: 12px; font-weight: 600; box-shadow: 0 2px 4px rgba(252, 177, 216, 0.4);">
<i class="bi bi-trophy-fill"></i> Best Seller
</span>
`
: ""
}
</div>
`;
}
document.getElementById("productDetail").innerHTML = `
<div style="font-family: 'Roboto', sans-serif;">
<nav style="background: #F6CCDE; padding: 16px 24px; box-shadow: 0 1px 3px rgba(252, 177, 216, 0.3);">
<div style="max-width: 1400px; margin: 0 auto; display: flex; align-items: center; gap: 20px;">
<a href="/home" style="font-size: 20px; font-weight: 600; color: #202023; text-decoration: none;">Sky Art Shop</a>
<span style="color: #202023; opacity: 0.5;">/</span>
<a href="/shop" style="color: #202023; opacity: 0.8; text-decoration: none; transition: opacity 0.3s;" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.8'">Shop</a>
<span style="color: #202023; opacity: 0.5;">/</span>
<span style="color: #202023; opacity: 0.7;">${
product.name
}</span>
</div>
</nav>
<div style="max-width: 1400px; margin: 40px auto; padding: 0 24px; background: #FFEBEB; border-radius: 12px; padding: 40px 24px;">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 60px;">
<!-- LEFT COLUMN: Image & Description -->
<div>
<!-- Back to Shop Link -->
<a href="/shop" style="display: inline-flex; align-items: center; gap: 8px; margin-bottom: 20px; color: #FCB1D8; text-decoration: none; font-weight: 600; font-size: 15px; transition: all 0.3s;"
onmouseover="this.style.gap='12px'; this.style.color='#d896c0'"
onmouseout="this.style.gap='8px'; this.style.color='#FCB1D8'">
<i class="bi bi-arrow-left"></i> Back to Shop
</a>
<!-- Image Section with Thumbnails -->
<div style="display: flex; gap: 16px;">
<!-- Thumbnail Gallery (Vertical Left) -->
${
imageGallery.length > 1
? `
<div id="thumbnailGallery" style="display: flex; flex-direction: column; gap: 12px; max-height: 600px; overflow-y: auto;">
${imageGallery
.map(
(img, idx) => `
<div onclick="changePrimaryImage('${
img.image_url
}', ${idx})"
class="thumbnail-item"
data-index="${idx}"
style="width: 70px; height: 70px; border-radius: 4px; overflow: hidden; cursor: pointer; border: ${
img.image_url === primaryImage
? "2px solid #6b46c1"
: "1px solid #e5e7eb"
}; transition: all 0.3s; flex-shrink: 0;">
<img src="${img.image_url}"
alt="${img.alt_text || product.name}"
style="width: 100%; height: 100%; object-fit: cover;"
onerror="this.src='/assets/images/placeholder.svg'" />
</div>
`
)
.join("")}
</div>
`
: ""
}
<!-- Main Product Image -->
<div style="flex: 1; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(252, 177, 216, 0.2); border: 1px solid #FFD0D0; padding: 20px; display: flex; align-items: center; justify-content: center; min-height: 500px; max-height: 600px;">
<img id="primaryImage"
src="${primaryImage}"
alt="${product.name}"
style="max-width: 100%; max-height: 560px; width: auto; height: auto; object-fit: contain; display: block;"
onerror="this.src='/assets/images/placeholder.svg'" />
</div>
</div>
<!-- Color Variants Section -->
${
imageGallery.some(
(img) => img.color_variant && img.color_code
)
? `
<div style="margin-top: 24px; padding: 20px; background: #FFD0D0; border-radius: 12px;">
<h4 style="font-size: 14px; font-weight: 600; color: #1a1a1a; margin-bottom: 12px; display: flex; align-items: center; gap: 8px;">
<i class="bi bi-palette"></i>
Available Colors (${
imageGallery.filter((img) => img.color_variant).length
})
</h4>
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
${imageGallery
.filter((img) => img.color_variant && img.color_code)
.map(
(img, idx) => `
<div onclick="changePrimaryImage('${
img.image_url
}', ${imageGallery.indexOf(img)})"
class="color-variant-circle"
title="${img.color_variant}"
style="position: relative; width: 48px; height: 48px; border-radius: 50%; cursor: pointer; border: 3px solid ${
img.image_url === primaryImage
? "#FCB1D8"
: "#FFEBEB"
}; transition: all 0.3s; overflow: hidden; box-shadow: 0 2px 4px rgba(252, 177, 216, 0.3);"
onmouseover="this.querySelector('.color-name-tooltip').style.opacity='1'; this.querySelector('.color-name-tooltip').style.transform='translateY(0)'; this.style.transform='scale(1.15)'"
onmouseout="this.querySelector('.color-name-tooltip').style.opacity='0'; this.querySelector('.color-name-tooltip').style.transform='translateY(-5px)'; this.style.transform='scale(1)'">
<div style="width: 100%; height: 100%; background: ${
img.color_code
};"></div>
<div class="color-name-tooltip" style="position: absolute; bottom: -35px; left: 50%; transform: translateX(-50%) translateY(-5px); background: rgba(0,0,0,0.9); color: white; padding: 6px 12px; border-radius: 6px; font-size: 12px; white-space: nowrap; pointer-events: none; opacity: 0; transition: all 0.3s; z-index: 10;">
${img.color_variant}
</div>
</div>
`
)
.join("")}
</div>
</div>
`
: ""
}
<!-- Description Box -->
${
product.description
? `
<div style="margin-top: 24px; padding: 20px; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(252, 177, 216, 0.2); border: 1px solid #FFD0D0;">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 2px solid #FCB1D8;">
<i class="bi bi-pin-angle-fill" style="color: #FCB1D8; font-size: 18px;"></i>
<h3 style="font-size: 16px; font-weight: 600; color: #202023; margin: 0;">Description</h3>
</div>
<div style="max-height: 200px; overflow-y: auto; color: #4b5563; line-height: 1.7; font-size: 15px; padding-right: 8px;">
${product.description}
</div>
</div>
`
: ""
}
</div>
<!-- RIGHT COLUMN: Product Info & Actions -->
<div style="padding: 20px 0;">
${badgesHTML}
<h1 style="font-size: 36px; font-weight: 700; color: #1a1a1a; margin: 0 0 16px 0; line-height: 1.2;">${
product.name
}</h1>
<div style="display: flex; align-items: baseline; gap: 16px; margin-bottom: 24px;">
<p style="font-size: 36px; font-weight: 700; color: #FCB1D8; margin: 0;">$${parseFloat(
product.price
).toFixed(2)}</p>
${
product.stockquantity > 0
? `<span style="color: #10b981; font-weight: 500; display: flex; align-items: center; gap: 6px;"><i class="bi bi-check-circle-fill"></i> In Stock (${product.stockquantity} available)</span>`
: `<span style="color: #ef4444; font-weight: 500; display: flex; align-items: center; gap: 6px;"><i class="bi bi-x-circle-fill"></i> Out of Stock</span>`
}
</div>
${
product.shortdescription
? `
<p style="font-size: 18px; color: #4b5563; line-height: 1.6; margin-bottom: 24px;">${product.shortdescription}</p>
`
: ""
}
${
product.category
? `
<p style="margin-bottom: 24px;">
<span style="font-weight: 500; color: #6b7280;">Category:</span>
<span style="display: inline-block; margin-left: 8px; padding: 6px 14px; background: #FFD0D0; border-radius: 6px; font-size: 14px; color: #202023;">
<i class="bi bi-tag"></i> ${product.category}
</span>
</p>
`
: ""
}
<!-- Product Details (Moved from description area) -->
${detailsHTML}
<!-- Add to Cart & Wishlist Buttons -->
<div style="display: flex; gap: 12px; margin-top: 32px; margin-bottom: 24px;">
<button onclick="addToCart()"
${product.stockquantity <= 0 ? "disabled" : ""}
style="padding: 14px 24px; background: ${
product.stockquantity <= 0 ? "#9ca3af" : "#FCB1D8"
}; color: #202023; border: none; border-radius: 10px; font-size: 15px; font-weight: 600; cursor: ${
product.stockquantity <= 0 ? "not-allowed" : "pointer"
}; transition: all 0.3s; display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 4px 12px rgba(252, 177, 216, 0.3);"
onmouseover="if(${
product.stockquantity > 0
}) this.style.transform='translateY(-2px)'; this.style.background='#F6CCDE'; this.style.boxShadow='0 6px 16px rgba(252, 177, 216, 0.4)'"
onmouseout="if(${
product.stockquantity > 0
}) this.style.transform='translateY(0)'; this.style.background='#FCB1D8'; this.style.boxShadow='0 4px 12px rgba(252, 177, 216, 0.3)'">
<i class="bi bi-cart-plus" style="font-size: 20px;"></i>
${
product.stockquantity <= 0
? "Out of Stock"
: "Add to Cart"
}
</button>
<button onclick="addToWishlist()"
style="padding: 14px 20px; background: white; color: #FCB1D8; border: 2px solid #FCB1D8; border-radius: 10px; font-size: 18px; cursor: pointer; transition: all 0.3s;"
onmouseover="this.style.background='#FCB1D8'; this.style.color='white'; this.style.transform='scale(1.05)'"
onmouseout="this.style.background='white'; this.style.color='#FCB1D8'; this.style.transform='scale(1)'">
<i class="bi bi-heart"></i>
</button>
</div>
</div>
</div>
<!-- Related Products Section -->
<div id="relatedProducts" style="margin-top: 60px; padding-top: 40px; border-top: 2px solid #e5e7eb;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 32px;">
<h2 style="font-size: 28px; font-weight: 700; color: #1a1a1a; margin: 0; display: flex; align-items: center; gap: 12px;">
<i class="bi bi-box-seam" style="color: #6b46c1;"></i>
You May Also Like
</h2>
</div>
<div id="relatedProductsGrid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 24px;">
<!-- Products will be loaded here -->
</div>
</div>
</div>
</div>
`;
document.getElementById("loading").style.display = "none";
document.getElementById("productDetail").style.display = "block";
// Store product data with imageurl for shopping cart
window.currentProduct = {
...product,
imageurl: primaryImage, // Add the primary image URL for cart display
};
// Track viewed product for smart recommendations
trackViewedProduct({
...product,
imageurl: primaryImage,
});
// Load related products
loadRelatedProducts(product.category, product.id);
console.log("Product loaded successfully:", product.name);
} catch (error) {
console.error("Error loading product:", error);
document.getElementById("loading").innerHTML =
'<div style="text-align: center; padding: 40px;"><p style="color: #ef4444; font-size: 18px; margin-bottom: 16px;">Error loading product</p><p style="color: #6b7280; margin-bottom: 20px;">' +
error.message +
'</p><a href="/shop" style="color: #FCB1D8; text-decoration: none; font-weight: 600;">← Back to Shop</a></div>';
}
}
function addToCart() {
if (!window.currentProduct) {
alert("Product not loaded. Please refresh the page.");
return;
}
window.ShopSystem.addToCart(window.currentProduct, 1);
}
function addToWishlist() {
if (!window.currentProduct) {
alert("Product not loaded. Please refresh the page.");
return;
}
window.ShopSystem.addToWishlist(window.currentProduct);
}
// Track viewed products for smart recommendations
function trackViewedProduct(product) {
try {
let viewedProducts = JSON.parse(
localStorage.getItem("skyart_viewed_products") || "[]"
);
// Remove if already exists (to update timestamp)
viewedProducts = viewedProducts.filter((p) => p.id !== product.id);
// Add to beginning of array
viewedProducts.unshift({
id: product.id,
name: product.name,
category: product.category,
imageurl: product.imageurl,
price: product.price,
viewedAt: new Date().toISOString(),
});
// Keep only last 20 viewed products
viewedProducts = viewedProducts.slice(0, 20);
localStorage.setItem(
"skyart_viewed_products",
JSON.stringify(viewedProducts)
);
} catch (e) {
console.error("Error tracking viewed product:", e);
}
}
// Load related products (same category + recently viewed)
async function loadRelatedProducts(category, currentProductId) {
try {
const container = document.getElementById("relatedProductsGrid");
if (!container) return;
// Show loading
container.innerHTML =
'<div style="grid-column: 1/-1; text-align: center; padding: 40px; color: #6b7280;">Loading recommendations...</div>';
// Fetch products from same category
const response = await window.apiCache.fetch("/api/products");
const data = await response.json();
if (data.success && data.products) {
let relatedProducts = [];
// Get products from same category (excluding current)
const sameCategoryProducts = data.products.filter(
(p) => p.category === category && p.id !== currentProductId
);
// Get recently viewed products
const viewedProducts = JSON.parse(
localStorage.getItem("skyart_viewed_products") || "[]"
);
const viewedIds = viewedProducts
.map((p) => p.id)
.filter((id) => id !== currentProductId);
const recentlyViewedProducts = data.products.filter(
(p) => viewedIds.includes(p.id) && p.id !== currentProductId
);
// Combine: prioritize same category, then recently viewed
relatedProducts = [...sameCategoryProducts];
recentlyViewedProducts.forEach((p) => {
if (!relatedProducts.find((rp) => rp.id === p.id)) {
relatedProducts.push(p);
}
});
// Shuffle and limit to 4-8 products
relatedProducts = shuffleArray(relatedProducts).slice(0, 8);
if (relatedProducts.length === 0) {
container.innerHTML =
'<div style="grid-column: 1/-1; text-align: center; padding: 40px; color: #6b7280;">No related products found.</div>';
return;
}
// Render products
container.innerHTML = relatedProducts
.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 `
<a href="/product?id=${
product.id
}" style="text-decoration: none; color: inherit; display: block; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.08); transition: all 0.3s; border: 1px solid #e5e7eb;"
onmouseover="this.style.transform='translateY(-4px)'; this.style.boxShadow='0 8px 16px rgba(0,0,0,0.12)'"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 8px rgba(0,0,0,0.08)'">
<div style="aspect-ratio: 1; overflow: hidden; background: #f9fafb;">
<img src="${productImage}"
alt="${product.name}"
style="width: 100%; height: 100%; object-fit: cover;"
onerror="this.src='/assets/images/placeholder.svg'" />
</div>
<div style="padding: 16px;">
<h3 style="font-size: 16px; font-weight: 600; color: #1a1a1a; margin: 0 0 8px 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
${product.name}
</h3>
${
product.shortdescription || product.description
? `
<p style="font-size: 14px; color: #636e72; margin: 0 0 12px 0; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;">
${
product.shortdescription ||
(product.description
? product.description.substring(0, 80) + "..."
: "")
}
</p>
`
: ""
}
${
product.category
? `
<p style="font-size: 13px; color: #6b7280; margin: 0 0 12px 0;">
<i class="bi bi-tag"></i> ${product.category}
</p>
`
: ""
}
<div style="display: flex; align-items: center; justify-content: space-between;">
<p style="font-size: 20px; font-weight: 700; color: #6b46c1; margin: 0;">
$${parseFloat(product.price).toFixed(2)}
</p>
${
product.stockquantity > 0
? '<span style="font-size: 12px; color: #10b981; font-weight: 500;"><i class="bi bi-check-circle-fill"></i> In Stock</span>'
: '<span style="font-size: 12px; color: #ef4444; font-weight: 500;"><i class="bi bi-x-circle-fill"></i> Out of Stock</span>'
}
</div>
</div>
</a>
`;
})
.join("");
}
} catch (error) {
console.error("Error loading related products:", error);
const container = document.getElementById("relatedProductsGrid");
if (container) {
container.innerHTML =
'<div style="grid-column: 1/-1; text-align: center; padding: 40px; color: #ef4444;">Error loading recommendations.</div>';
}
}
}
// Shuffle array utility
function shuffleArray(array) {
const arr = [...array];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
loadProduct();
</script>
<!-- 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>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,260 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Privacy Policy - Sky Art Shop</title>
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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" />
</head>
<body>
<script>window.__bodyReady=true</script>
<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 active">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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1><i class="bi bi-shield-check"></i> Privacy Policy</h1>
<p class="hero-subtitle">How we protect and use your information</p>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<div class="container">
<div style="max-width: 800px; margin: 0 auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<p style="color: #666; line-height: 1.8; margin-bottom: 30px;"><em>Last updated: January 14, 2026</em></p>
<h2 style="color: #333; margin-bottom: 15px;">Information We Collect</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 20px;">We collect information you provide directly to us, including your name, email address, shipping address, and payment information when you make a purchase.</p>
<h2 style="color: #333; margin-bottom: 15px; margin-top: 30px;">How We Use Your Information</h2>
<ul style="color: #666; line-height: 2; margin-left: 20px;">
<li>Process and fulfill your orders</li>
<li>Send you order confirmations and shipping updates</li>
<li>Respond to your questions and provide customer support</li>
<li>Send you promotional emails (with your consent)</li>
<li>Improve our website and services</li>
</ul>
<h2 style="color: #333; margin-bottom: 15px; margin-top: 30px;">Data Security</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 20px;">We use industry-standard SSL encryption to protect your personal and payment information. We never store credit card numbers on our servers.</p>
<h2 style="color: #333; margin-bottom: 15px; margin-top: 30px;">Your Rights</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 20px;">You have the right to access, correct, or delete your personal information. You can also unsubscribe from marketing emails at any time.</p>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid #667eea; margin-top: 30px;">
<p style="color: #666; margin: 0;"><strong>Privacy questions?</strong> Email us at <a href="mailto:privacy@skyartshop.com" style="color: #667eea;">privacy@skyartshop.com</a></p>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core Scripts (standardized order) -->
<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 src="/assets/js/navigation.js"></script>
</body>
</html>

View File

@@ -1,260 +1,455 @@
<!DOCTYPE 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="Privacy Policy - Sky Art Shop" />
<title>Privacy Policy - Sky Art Shop</title>
<!-- Fonts -->
<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"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap"
rel="stylesheet"
/>
<!-- Icons -->
<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=1768447584" />
<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" />
<!-- Modern Theme CSS -->
<link
rel="stylesheet"
href="/assets/css/modern-theme.css?v=20260118drawer"
/>
<link
rel="stylesheet"
href="/assets/css/mobile-fixes.css?v=20260118editor"
/>
<style>
.policy-container {
max-width: 900px;
margin: 0 auto;
background: var(--bg-white);
padding: var(--spacing-2xl);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
}
.policy-container h2 {
font-family: var(--font-heading);
font-size: 1.75rem;
margin-top: var(--spacing-xl);
margin-bottom: var(--spacing-md);
color: var(--text-primary);
}
.policy-container h2:first-of-type {
margin-top: 0;
}
.policy-container p {
color: var(--text-secondary);
line-height: 1.8;
margin-bottom: var(--spacing-lg);
}
.policy-container ul {
color: var(--text-secondary);
line-height: 2;
margin-left: var(--spacing-xl);
margin-bottom: var(--spacing-lg);
}
.policy-container ul li {
margin-bottom: var(--spacing-sm);
}
.policy-meta {
color: var(--text-light);
font-style: italic;
margin-bottom: var(--spacing-xl);
padding: var(--spacing-md);
background: var(--primary-pink-light);
border-radius: var(--radius-sm);
}
.contact-box {
background: var(--primary-pink-light);
padding: var(--spacing-xl);
border-radius: var(--radius-lg);
margin-top: var(--spacing-2xl);
text-align: center;
}
</style>
</head>
<body>
<script>window.__bodyReady=true</script>
<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>
<!-- Navigation -->
<header class="nav-wrapper">
<nav class="navbar">
<a href="/home" class="nav-brand">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop Logo"
/>
<span>Sky Art Shop</span>
</a>
<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 active">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 class="nav-menu">
<li><a href="/home" class="nav-link">Home</a></li>
<li><a href="/shop" class="nav-link">Shop</a></li>
<li><a href="/portfolio" class="nav-link">Portfolio</a></li>
<li><a href="/blog" class="nav-link">Blog</a></li>
<li><a href="/about" class="nav-link">About</a></li>
<li><a href="/contact" class="nav-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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1><i class="bi bi-shield-check"></i> Privacy Policy</h1>
<p class="hero-subtitle">How we protect and use your information</p>
<div class="nav-actions">
<a href="/signin" class="nav-icon-btn" title="Sign In">
<i class="bi bi-person"></i>
</a>
<button class="nav-icon-btn wishlist-btn-nav" title="Wishlist">
<i class="bi bi-heart"></i>
<span class="badge wishlist-count" style="display: none">0</span>
</button>
<button class="nav-icon-btn cart-btn" title="Cart">
<i class="bi bi-bag"></i>
<span class="badge cart-count" style="display: none">0</span>
</button>
<button class="nav-mobile-toggle" aria-label="Toggle menu">
<span></span>
<span></span>
<span></span>
</button>
</div>
</nav>
</header>
<!-- Page Content -->
<main class="page-content">
<!-- Page Header -->
<div class="page-header">
<div class="container">
<h1 id="privacyHeaderTitle">
<i class="bi bi-shield-check"></i> Privacy Policy
</h1>
<p>How we protect and use your information</p>
<div class="breadcrumb">
<a href="/home">Home</a>
<span>/</span>
<span>Privacy Policy</span>
</div>
</div>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<!-- Policy Content -->
<section class="section">
<div class="container">
<div class="policy-container" id="privacyContainer">
<!-- Content will be loaded dynamically from the database -->
<div
class="loading-placeholder"
style="text-align: center; padding: 2rem"
>
<i
class="bi bi-hourglass-split"
style="font-size: 2rem; animation: spin 1s linear infinite"
></i>
<p>Loading privacy policy...</p>
</div>
</div>
</div>
</section>
</main>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div style="max-width: 800px; margin: 0 auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<p style="color: #666; line-height: 1.8; margin-bottom: 30px;"><em>Last updated: January 14, 2026</em></p>
<div class="footer-grid">
<div class="footer-about">
<div class="footer-brand">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop"
/>
<span>Sky Art Shop</span>
</div>
<p>
Your one-stop shop for scrapbooking, journaling, cardmaking, and
collaging stationery. Quality products for all your creative
needs.
</p>
<div class="footer-social">
<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-pinterest"></i
></a>
<a href="#" class="social-link"><i class="bi bi-youtube"></i></a>
</div>
</div>
<div class="footer-column">
<h4>Quick Links</h4>
<ul class="footer-links">
<li><a href="/home">Home</a></li>
<li><a href="/shop">Shop</a></li>
<li><a href="/portfolio">Portfolio</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/about">About Us</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Customer Service</h4>
<ul class="footer-links">
<li><a href="/faq">FAQ</a></li>
<li><a href="/shipping-info">Shipping Info</a></li>
<li><a href="/returns">Returns & Refunds</a></li>
<li><a href="/privacy">Privacy Policy</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Contact Us</h4>
<ul class="footer-links">
<li><i class="bi bi-envelope"></i> hello@skyartshop.com</li>
<li><i class="bi bi-telephone"></i> (555) 123-4567</li>
<li><i class="bi bi-geo-alt"></i> 123 Creative Lane</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2026 Sky Art Shop. All rights reserved.</p>
<p>
Made with
<i class="bi bi-heart-fill" style="color: var(--primary-pink)"></i>
for creative souls
</p>
</div>
</div>
</footer>
<!-- Cart Drawer -->
<div class="cart-overlay"></div>
<div class="cart-drawer">
<div class="cart-header">
<h3>Shopping Cart</h3>
<button class="cart-close"><i class="bi bi-x"></i></button>
</div>
<div class="cart-items">
<!-- Cart items rendered via JavaScript -->
</div>
<div class="cart-footer">
<div class="cart-total">
<span>Total:</span>
<span class="cart-total-amount">$0.00</span>
</div>
<a href="/checkout" class="btn btn-primary cart-checkout-btn"
>Checkout</a
>
</div>
</div>
<!-- Wishlist Drawer -->
<div class="wishlist-overlay"></div>
<div class="wishlist-drawer">
<div class="wishlist-header">
<h3><i class="bi bi-heart"></i> My Wishlist</h3>
<button class="wishlist-close"><i class="bi bi-x"></i></button>
</div>
<div class="wishlist-items"></div>
<div class="wishlist-footer">
<a href="/shop" class="btn btn-outline">Continue Shopping</a>
</div>
</div>
<!-- Scripts -->
<script src="/assets/js/modern-theme.js?v=20260118c"></script>
<script src="/assets/js/customer-auth.js"></script>
<script src="/assets/js/accessibility.js"></script>
<script>
// Load privacy page data from API
async function loadPrivacyPageData() {
try {
console.log("Loading privacy page data from API...");
const response = await fetch("/api/pages/privacy?" + Date.now(), {
cache: "no-cache",
headers: {
"Cache-Control": "no-cache",
},
});
if (!response.ok) {
console.log("Privacy page not found in database, using defaults");
renderDefaultPrivacyContent();
return;
}
const data = await response.json();
console.log("Received data from API:", data);
const pageData = data.page.pagedata || {};
console.log("Page data:", pageData);
// Update header if data exists
if (pageData.header) {
if (pageData.header.title) {
document.getElementById("privacyHeaderTitle").innerHTML =
'<i class="bi bi-shield-check"></i> ' + pageData.header.title;
}
if (pageData.header.subtitle) {
const subtitleEl = document.querySelector(".page-header p");
if (subtitleEl) {
subtitleEl.textContent = pageData.header.subtitle;
}
}
}
// Render content
const container = document.getElementById("privacyContainer");
let contentHtml = "";
// Add last updated date if exists
if (pageData.lastUpdated) {
contentHtml += `<p class="policy-meta">Last updated: ${escapeHtml(pageData.lastUpdated)}</p>`;
}
// Add main content (new format)
if (pageData.mainContent) {
console.log(
"Using mainContent:",
pageData.mainContent.substring(0, 100) + "...",
);
contentHtml += pageData.mainContent;
}
// Fallback to sections (old format)
else if (pageData.sections && pageData.sections.length > 0) {
console.log(
"Using sections format, sections:",
pageData.sections.length,
);
contentHtml += pageData.sections
.map((section) => {
let sectionHtml = "";
if (section.title) {
sectionHtml += `<h2>${escapeHtml(section.title)}</h2>`;
}
if (section.content) {
sectionHtml += section.content;
}
return sectionHtml;
})
.join("");
}
// Add contact box at the end
if (pageData.contactBox) {
const box = pageData.contactBox;
contentHtml += `
<div class="contact-box">
<h3>${escapeHtml(box.title || "Privacy Questions?")}</h3>
<p>${escapeHtml(box.message || "If you have any questions about this Privacy Policy, please contact us:")}</p>
<div style="margin-top: var(--spacing-lg)">
<p><strong>Email:</strong> ${escapeHtml(box.email || "privacy@skyartshop.com")}</p>
<p><strong>Phone:</strong> ${escapeHtml(box.phone || "(555) 123-4567")}</p>
<p><strong>Mail:</strong> ${escapeHtml(box.address || "Sky Art Shop, 123 Creative Lane, City, ST 12345")}</p>
</div>
</div>
`;
}
// Fallback to hardcoded contact box (old format)
else if (pageData.sections && pageData.sections.length > 0) {
contentHtml += `
<div class="contact-box">
<h3>Privacy Questions?</h3>
<p>If you have any questions about this Privacy Policy, please contact us:</p>
<div style="margin-top: var(--spacing-lg)">
<p><strong>Email:</strong> privacy@skyartshop.com</p>
<p><strong>Phone:</strong> (555) 123-4567</p>
<p><strong>Mail:</strong> Sky Art Shop, 123 Creative Lane, City, ST 12345</p>
</div>
</div>
`;
}
// If we have any content, show it, otherwise use defaults
if (contentHtml.trim()) {
container.innerHTML = contentHtml;
} else {
renderDefaultPrivacyContent();
}
} catch (error) {
console.error("Error loading privacy page:", error);
renderDefaultPrivacyContent();
}
}
function renderDefaultPrivacyContent() {
const container = document.getElementById("privacyContainer");
container.innerHTML = `
<p class="policy-meta">Last updated: January 14, 2026</p>
<h2 style="color: #333; margin-bottom: 15px;">Information We Collect</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 20px;">We collect information you provide directly to us, including your name, email address, shipping address, and payment information when you make a purchase.</p>
<p>At Sky Art Shop, we take your privacy seriously. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and make purchases from our store.</p>
<h2 style="color: #333; margin-bottom: 15px; margin-top: 30px;">How We Use Your Information</h2>
<ul style="color: #666; line-height: 2; margin-left: 20px;">
<h2>Information We Collect</h2>
<p>We collect information you provide directly to us when you:</p>
<ul>
<li>Create an account or make a purchase</li>
<li>Subscribe to our newsletter</li>
<li>Contact our customer service</li>
<li>Participate in surveys or promotions</li>
<li>Post reviews or comments</li>
</ul>
<p>This information may include your name, email address, shipping address, phone number, and payment information.</p>
<h2>How We Use Your Information</h2>
<p>We use the information we collect to:</p>
<ul>
<li>Process and fulfill your orders</li>
<li>Send you order confirmations and shipping updates</li>
<li>Respond to your questions and provide customer support</li>
<li>Send you promotional emails (with your consent)</li>
<li>Improve our website and services</li>
<li>Prevent fraud and enhance security</li>
<li>Comply with legal obligations</li>
</ul>
<h2 style="color: #333; margin-bottom: 15px; margin-top: 30px;">Data Security</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 20px;">We use industry-standard SSL encryption to protect your personal and payment information. We never store credit card numbers on our servers.</p>
<h2>Data Security</h2>
<p>We implement industry-standard security measures to protect your personal information:</p>
<ul>
<li>SSL/TLS encryption for all data transmission</li>
<li>Secure payment processing through PCI-compliant providers</li>
<li>Regular security audits and updates</li>
<li>Limited access to personal information by authorized personnel only</li>
</ul>
<h2 style="color: #333; margin-bottom: 15px; margin-top: 30px;">Your Rights</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 20px;">You have the right to access, correct, or delete your personal information. You can also unsubscribe from marketing emails at any time.</p>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid #667eea; margin-top: 30px;">
<p style="color: #666; margin: 0;"><strong>Privacy questions?</strong> Email us at <a href="mailto:privacy@skyartshop.com" style="color: #667eea;">privacy@skyartshop.com</a></p>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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 class="contact-box">
<h3>Privacy Questions?</h3>
<p>If you have any questions about this Privacy Policy, please contact us:</p>
<div style="margin-top: var(--spacing-lg)">
<p><strong>Email:</strong> privacy@skyartshop.com</p>
<p><strong>Phone:</strong> (555) 123-4567</p>
<p><strong>Mail:</strong> Sky Art Shop, 123 Creative Lane, City, ST 12345</p>
</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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core Scripts (standardized order) -->
<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 src="/assets/js/navigation.js"></script>
`;
}
function escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
// Load on page ready
document.addEventListener("DOMContentLoaded", loadPrivacyPageData);
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,260 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Returns & Refunds - Sky Art Shop</title>
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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" />
</head>
<body>
<script>window.__bodyReady=true</script>
<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 active">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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1><i class="bi bi-arrow-left-right"></i> Returns & Refunds</h1>
<p class="hero-subtitle">30-day hassle-free return policy</p>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<div class="container">
<div style="max-width: 800px; margin: 0 auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<h2 style="color: #333; margin-bottom: 20px;"><i class="bi bi-check-circle" style="color: #667eea;"></i> 30-Day Return Policy</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 20px;">We want you to love your purchase! If you're not completely satisfied, you can return most items within 30 days of delivery for a full refund.</p>
<h3 style="color: #667eea; font-size: 18px; margin-top: 30px;">How to Return an Item</h3>
<ol style="color: #666; line-height: 2; margin-left: 20px;">
<li>Contact our customer service team to initiate a return</li>
<li>Pack the item securely in its original packaging</li>
<li>Include your order number and reason for return</li>
<li>Ship the package using our prepaid return label</li>
<li>Refund will be processed within 5-7 business days after we receive the item</li>
</ol>
<h3 style="color: #667eea; font-size: 18px; margin-top: 30px;">Return Requirements</h3>
<ul style="color: #666; line-height: 2; margin-left: 20px;">
<li>Item must be unused and in original condition</li>
<li>Original packaging and tags must be intact</li>
<li>Custom or personalized items cannot be returned</li>
<li>Sale items are final sale unless defective</li>
</ul>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid #667eea; margin-top: 30px;">
<p style="color: #666; margin: 0;"><strong>Questions about returns?</strong> Contact us at <a href="mailto:returns@skyartshop.com" style="color: #667eea;">returns@skyartshop.com</a> or call 1-800-SKY-ARTS</p>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core Scripts (standardized order) -->
<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 src="/assets/js/navigation.js"></script>
</body>
</html>

View File

@@ -1,260 +1,400 @@
<!DOCTYPE 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="Returns & Refunds Policy - Sky Art Shop"
/>
<title>Returns & Refunds - Sky Art Shop</title>
<!-- Fonts -->
<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"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap"
rel="stylesheet"
/>
<!-- Icons -->
<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=1768447584" />
<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" />
<!-- Modern Theme CSS -->
<link
rel="stylesheet"
href="/assets/css/modern-theme.css?v=20260118drawer"
/>
<link
rel="stylesheet"
href="/assets/css/mobile-fixes.css?v=20260118editor"
/>
<style>
.policy-container {
max-width: 900px;
margin: 0 auto;
background: var(--bg-white);
padding: var(--spacing-2xl);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
}
.policy-container h2 {
font-family: var(--font-heading);
font-size: 1.75rem;
margin-top: var(--spacing-xl);
margin-bottom: var(--spacing-md);
color: var(--text-primary);
}
.policy-container h2:first-of-type {
margin-top: 0;
}
.policy-container p {
color: var(--text-secondary);
line-height: 1.8;
margin-bottom: var(--spacing-lg);
}
.policy-container ul,
.policy-container ol {
color: var(--text-secondary);
line-height: 2;
margin-left: var(--spacing-xl);
margin-bottom: var(--spacing-lg);
}
.policy-container ul li,
.policy-container ol li {
margin-bottom: var(--spacing-sm);
}
.highlight-box {
background: var(--primary-pink-light);
padding: var(--spacing-lg);
border-radius: var(--radius-md);
margin: var(--spacing-lg) 0;
border-left: 4px solid var(--primary-pink-dark);
}
.steps-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--spacing-lg);
margin: var(--spacing-xl) 0;
}
.step-card {
text-align: center;
padding: var(--spacing-lg);
background: var(--bg-light);
border-radius: var(--radius-lg);
}
.step-number {
width: 50px;
height: 50px;
margin: 0 auto var(--spacing-md);
background: var(--primary-pink-dark);
color: var(--text-white);
border-radius: var(--radius-full);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
font-weight: 700;
}
</style>
</head>
<body>
<script>window.__bodyReady=true</script>
<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>
<!-- Navigation -->
<header class="nav-wrapper">
<nav class="navbar">
<a href="/home" class="nav-brand">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop Logo"
/>
<span>Sky Art Shop</span>
</a>
<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 active">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 class="nav-menu">
<li><a href="/home" class="nav-link">Home</a></li>
<li><a href="/shop" class="nav-link">Shop</a></li>
<li><a href="/portfolio" class="nav-link">Portfolio</a></li>
<li><a href="/blog" class="nav-link">Blog</a></li>
<li><a href="/about" class="nav-link">About</a></li>
<li><a href="/contact" class="nav-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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1><i class="bi bi-arrow-left-right"></i> Returns & Refunds</h1>
<p class="hero-subtitle">30-day hassle-free return policy</p>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<div class="container">
<div style="max-width: 800px; margin: 0 auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<h2 style="color: #333; margin-bottom: 20px;"><i class="bi bi-check-circle" style="color: #667eea;"></i> 30-Day Return Policy</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 20px;">We want you to love your purchase! If you're not completely satisfied, you can return most items within 30 days of delivery for a full refund.</p>
<h3 style="color: #667eea; font-size: 18px; margin-top: 30px;">How to Return an Item</h3>
<ol style="color: #666; line-height: 2; margin-left: 20px;">
<li>Contact our customer service team to initiate a return</li>
<li>Pack the item securely in its original packaging</li>
<li>Include your order number and reason for return</li>
<li>Ship the package using our prepaid return label</li>
<li>Refund will be processed within 5-7 business days after we receive the item</li>
</ol>
<h3 style="color: #667eea; font-size: 18px; margin-top: 30px;">Return Requirements</h3>
<ul style="color: #666; line-height: 2; margin-left: 20px;">
<li>Item must be unused and in original condition</li>
<li>Original packaging and tags must be intact</li>
<li>Custom or personalized items cannot be returned</li>
<li>Sale items are final sale unless defective</li>
</ul>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid #667eea; margin-top: 30px;">
<p style="color: #666; margin: 0;"><strong>Questions about returns?</strong> Contact us at <a href="mailto:returns@skyartshop.com" style="color: #667eea;">returns@skyartshop.com</a> or call 1-800-SKY-ARTS</p>
<div class="nav-actions">
<a href="/signin" class="nav-icon-btn" title="Sign In">
<i class="bi bi-person"></i>
</a>
<button class="nav-icon-btn wishlist-btn-nav" title="Wishlist">
<i class="bi bi-heart"></i>
<span class="badge wishlist-count" style="display: none">0</span>
</button>
<button class="nav-icon-btn cart-btn" title="Cart">
<i class="bi bi-bag"></i>
<span class="badge cart-count" style="display: none">0</span>
</button>
<button class="nav-mobile-toggle" aria-label="Toggle menu">
<span></span>
<span></span>
<span></span>
</button>
</div>
</nav>
</header>
<!-- Page Content -->
<main class="page-content">
<!-- Page Header -->
<div class="page-header">
<div class="container">
<h1 id="returnsHeaderTitle">
<i class="bi bi-arrow-return-left"></i> Returns & Refunds
</h1>
<p id="returnsHeaderSubtitle">Our hassle-free return policy</p>
<div class="breadcrumb">
<a href="/home">Home</a>
<span>/</span>
<span>Returns & Refunds</span>
</div>
</div>
</div>
</section>
<!-- Policy Content -->
<section class="section">
<div class="container">
<div class="policy-container" id="returnsContainer">
<div class="loading-spinner" style="margin: 40px auto"></div>
</div>
</div>
</section>
</main>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">Sky Art Shop</h3>
<p class="footer-text">
Your destination for unique art pieces and creative supplies.
<div class="footer-about">
<div class="footer-brand">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop"
/>
<span>Sky Art Shop</span>
</div>
<p>
Your one-stop shop for scrapbooking, journaling, cardmaking, and
collaging stationery. Quality products for all your creative
needs.
</p>
<div class="social-links">
<div class="footer-social">
<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>
<a href="#" class="social-link"
><i class="bi bi-instagram"></i
></a>
<a href="#" class="social-link"
><i class="bi bi-pinterest"></i
></a>
<a href="#" class="social-link"><i class="bi bi-youtube"></i></a>
</div>
</div>
<div class="footer-col">
<h4 class="footer-heading">Shop</h4>
<div class="footer-column">
<h4>Quick Links</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="/home">Home</a></li>
<li><a href="/shop">Shop</a></li>
<li><a href="/portfolio">Portfolio</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/about">About Us</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<div class="footer-col">
<h4 class="footer-heading">Customer Service</h4>
<div class="footer-column">
<h4>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="/shipping-info">Shipping Info</a></li>
<li><a href="/returns">Returns & Refunds</a></li>
<li><a href="/privacy">Privacy Policy</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Contact Us</h4>
<ul class="footer-links">
<li><i class="bi bi-envelope"></i> hello@skyartshop.com</li>
<li><i class="bi bi-telephone"></i> (555) 123-4567</li>
<li><i class="bi bi-geo-alt"></i> 123 Creative Lane</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 Sky Art Shop. All rights reserved.</p>
<p>&copy; 2026 Sky Art Shop. All rights reserved.</p>
<p>
Made with
<i class="bi bi-heart-fill" style="color: var(--primary-pink)"></i>
for creative souls
</p>
</div>
</div>
</footer>
<!-- Core Scripts (standardized order) -->
<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 src="/assets/js/navigation.js"></script>
<!-- Cart Drawer -->
<div class="cart-overlay"></div>
<div class="cart-drawer">
<div class="cart-header">
<h3>Shopping Cart</h3>
<button class="cart-close"><i class="bi bi-x"></i></button>
</div>
<div class="cart-items">
<!-- Cart items rendered via JavaScript -->
</div>
<div class="cart-footer">
<div class="cart-total">
<span>Total:</span>
<span class="cart-total-amount">$0.00</span>
</div>
<a href="/checkout" class="btn btn-primary cart-checkout-btn"
>Checkout</a
>
</div>
</div>
<!-- Wishlist Drawer -->
<div class="wishlist-overlay"></div>
<div class="wishlist-drawer">
<div class="wishlist-header">
<h3><i class="bi bi-heart"></i> My Wishlist</h3>
<button class="wishlist-close"><i class="bi bi-x"></i></button>
</div>
<div class="wishlist-items"></div>
<div class="wishlist-footer">
<a href="/shop" class="btn btn-outline">Continue Shopping</a>
</div>
</div>
<!-- Scripts -->
<script src="/assets/js/modern-theme.js?v=20260118c"></script>
<script src="/assets/js/customer-auth.js"></script>
<script>
// Load returns page data from API
async function loadReturnsPageData() {
const container = document.getElementById("returnsContainer");
try {
const response = await fetch("/api/pages/returns");
const data = await response.json();
if (data.success && data.page && data.page.pagedata) {
const pagedata =
typeof data.page.pagedata === "string"
? JSON.parse(data.page.pagedata)
: data.page.pagedata;
// Update header
if (pagedata.header) {
if (pagedata.header.title) {
document.getElementById("returnsHeaderTitle").innerHTML =
'<i class="bi bi-arrow-return-left"></i> ' +
pagedata.header.title;
}
if (pagedata.header.subtitle) {
document.getElementById("returnsHeaderSubtitle").textContent =
pagedata.header.subtitle;
}
}
// Build content HTML
let html = "";
// Highlight box
if (pagedata.highlight) {
html += `
<div class="highlight-box">
<p style="margin: 0; font-weight: 600; font-size: 1.1rem">
<i class="bi bi-info-circle"></i> ${pagedata.highlight}
</p>
</div>
`;
}
// Render sections
if (pagedata.sections && pagedata.sections.length > 0) {
pagedata.sections.forEach((section) => {
html += `<h2>${escapeHtml(section.title)}</h2>`;
html += `<div>${section.content}</div>`;
});
}
// Contact box
html += `
<div class="highlight-box">
<h3 style="margin-top: 0">Need Help?</h3>
<p style="margin-bottom: var(--spacing-md)">
Our customer service team is here to assist you with any return questions.
</p>
<div style="display: flex; gap: var(--spacing-md); flex-wrap: wrap">
<a href="/contact" class="btn btn-primary">Contact Us</a>
<a href="mailto:support@skyartshop.com" class="btn btn-outline">Email Support</a>
</div>
</div>
`;
container.innerHTML = html;
} else {
// Show default content if no API data
showDefaultContent(container);
}
} catch (error) {
console.error("Error loading returns page:", error);
showDefaultContent(container);
}
}
function showDefaultContent(container) {
container.innerHTML = `
<div class="highlight-box">
<p style="margin: 0; font-weight: 600; font-size: 1.1rem">
<i class="bi bi-info-circle"></i> We want you to love your purchase!
If you're not completely satisfied, we offer a 30-day return policy on most items.
</p>
</div>
<h2>Return Policy</h2>
<p>Please contact us for our full return policy details.</p>
<div class="highlight-box">
<h3 style="margin-top: 0">Need Help?</h3>
<p style="margin-bottom: var(--spacing-md)">
Our customer service team is here to assist you.
</p>
<div style="display: flex; gap: var(--spacing-md); flex-wrap: wrap">
<a href="/contact" class="btn btn-primary">Contact Us</a>
</div>
</div>
`;
}
function escapeHtml(text) {
if (!text) return "";
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
document.addEventListener("DOMContentLoaded", loadReturnsPageData);
</script>
<script src="/assets/js/accessibility.js"></script>
</body>
</html>

View File

@@ -0,0 +1,253 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Shipping Information - Sky Art Shop</title>
<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=1768450104" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<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" />
</head>
<body>
<script>window.__bodyReady=true</script>
<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 active">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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1><i class="bi bi-truck"></i> Shipping Information</h1>
<p class="hero-subtitle">Fast, reliable delivery to your doorstep</p>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<div class="container">
<div style="max-width: 800px; margin: 0 auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 2 8px rgba(0,0,0,0.1);">
<h2 style="color: #333; margin-bottom: 20px;"><i class="bi bi-box-seam" style="color: #667eea;"></i> Shipping Methods</h2>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea; font-size: 18px;">Standard Shipping (5-7 Business Days)</h3>
<p style="color: #666; line-height: 1.8;">Our most economical shipping option. Orders are processed within 1-2 business days.</p>
<p style="color: #999; font-size: 14px;"><strong>Cost:</strong> $5.99 for orders under $50 | <strong>FREE</strong> for orders over $50</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea; font-size: 18px;">Express Shipping (2-3 Business Days)</h3>
<p style="color: #666; line-height: 1.8;">Faster delivery via UPS 2nd Day Air.</p>
<p style="color: #999; font-size: 14px;"><strong>Cost:</strong> $12.99 flat rate</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea; font-size: 18px;">Overnight Shipping</h3>
<p style="color: #666; line-height: 1.8;">Order by 2 PM EST for next business day delivery.</p>
<p style="color: #999; font-size: 14px;"><strong>Cost:</strong> $24.99 flat rate</p>
</div>
<h2 style="color: #333; margin-bottom: 20px; margin-top: 40px;"><i class="bi bi-globe" style="color: #667eea;"></i> International Shipping</h2>
<p style="color: #666; line-height: 1.8;">We ship to over 50 countries worldwide! International orders typically take 7-14 business days. Customs fees are the responsibility of the customer.</p>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">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>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core Scripts (standardized order) -->
<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 src="/assets/js/navigation.js"></script>
</body>
</html>

View File

@@ -1,253 +1,476 @@
<!DOCTYPE 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="Shipping Information - Sky Art Shop" />
<title>Shipping Information - Sky Art Shop</title>
<!-- Fonts -->
<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"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap"
rel="stylesheet"
/>
<!-- Icons -->
<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=1768447584" />
<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" />
<!-- Modern Theme CSS -->
<link
rel="stylesheet"
href="/assets/css/modern-theme.css?v=20260118drawer"
/>
<link
rel="stylesheet"
href="/assets/css/mobile-fixes.css?v=20260118editor"
/>
<style>
.policy-container {
max-width: 900px;
margin: 0 auto;
background: var(--bg-white);
padding: var(--spacing-2xl);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
}
.policy-container h2 {
font-family: var(--font-heading);
font-size: 1.75rem;
margin-top: var(--spacing-xl);
margin-bottom: var(--spacing-md);
color: var(--text-primary);
}
.policy-container h2:first-of-type {
margin-top: 0;
}
.policy-container p {
color: var(--text-secondary);
line-height: 1.8;
margin-bottom: var(--spacing-lg);
}
.policy-container ul {
color: var(--text-secondary);
line-height: 2;
margin-left: var(--spacing-xl);
margin-bottom: var(--spacing-lg);
}
.policy-container ul li {
margin-bottom: var(--spacing-sm);
}
.shipping-options {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-lg);
margin: var(--spacing-xl) 0;
}
.shipping-card {
background: var(--bg-light);
padding: var(--spacing-xl);
border-radius: var(--radius-lg);
text-align: center;
border: 2px solid var(--border-light);
transition: var(--transition-smooth);
}
.shipping-card:hover {
border-color: var(--primary-pink-dark);
box-shadow: var(--shadow-md);
}
.shipping-icon {
font-size: 3rem;
color: var(--primary-pink-dark);
margin-bottom: var(--spacing-md);
}
.shipping-card h3 {
font-size: 1.25rem;
margin-bottom: var(--spacing-sm);
}
.shipping-card .price {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-primary);
margin: var(--spacing-md) 0;
}
.highlight-box {
background: var(--primary-pink-light);
padding: var(--spacing-lg);
border-radius: var(--radius-md);
margin: var(--spacing-lg) 0;
border-left: 4px solid var(--primary-pink-dark);
}
.info-table {
width: 100%;
border-collapse: collapse;
margin: var(--spacing-lg) 0;
}
.info-table th,
.info-table td {
padding: var(--spacing-md);
text-align: left;
border-bottom: 1px solid var(--border-light);
}
.info-table th {
background: var(--bg-light);
font-weight: 600;
color: var(--text-primary);
}
.info-table td {
color: var(--text-secondary);
}
</style>
</head>
<body>
<script>window.__bodyReady=true</script>
<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>
<!-- Navigation -->
<header class="nav-wrapper">
<nav class="navbar">
<a href="/home" class="nav-brand">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop Logo"
/>
<span>Sky Art Shop</span>
</a>
<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 active">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 class="nav-menu">
<li><a href="/home" class="nav-link">Home</a></li>
<li><a href="/shop" class="nav-link">Shop</a></li>
<li><a href="/portfolio" class="nav-link">Portfolio</a></li>
<li><a href="/blog" class="nav-link">Blog</a></li>
<li><a href="/about" class="nav-link">About</a></li>
<li><a href="/contact" class="nav-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);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileMenu.classList.contains('active')) closeMenu();
});
})();
</script>
<section class="about-hero">
<div class="container">
<h1><i class="bi bi-truck"></i> Shipping Information</h1>
<p class="hero-subtitle">Fast, reliable delivery to your doorstep</p>
</div>
</section>
<section class="blog-section" style="padding: 60px 0; background: #ffebeb">
<div class="container">
<div style="max-width: 800px; margin: 0 auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 2 8px rgba(0,0,0,0.1);">
<h2 style="color: #333; margin-bottom: 20px;"><i class="bi bi-box-seam" style="color: #667eea;"></i> Shipping Methods</h2>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea; font-size: 18px;">Standard Shipping (5-7 Business Days)</h3>
<p style="color: #666; line-height: 1.8;">Our most economical shipping option. Orders are processed within 1-2 business days.</p>
<p style="color: #999; font-size: 14px;"><strong>Cost:</strong> $5.99 for orders under $50 | <strong>FREE</strong> for orders over $50</p>
<div class="nav-actions">
<a href="/signin" class="nav-icon-btn" title="Sign In">
<i class="bi bi-person"></i>
</a>
<button class="nav-icon-btn wishlist-btn-nav" title="Wishlist">
<i class="bi bi-heart"></i>
<span class="badge wishlist-count" style="display: none">0</span>
</button>
<button class="nav-icon-btn cart-btn" title="Cart">
<i class="bi bi-bag"></i>
<span class="badge cart-count" style="display: none">0</span>
</button>
<button class="nav-mobile-toggle" aria-label="Toggle menu">
<span></span>
<span></span>
<span></span>
</button>
</div>
</nav>
</header>
<!-- Page Content -->
<main class="page-content">
<!-- Page Header -->
<div class="page-header">
<div class="container">
<h1 id="shippingHeaderTitle">
<i class="bi bi-truck"></i> Shipping Information
</h1>
<p id="shippingHeaderSubtitle">Fast and reliable shipping options</p>
<div class="breadcrumb">
<a href="/home">Home</a>
<span>/</span>
<span>Shipping Information</span>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea; font-size: 18px;">Express Shipping (2-3 Business Days)</h3>
<p style="color: #666; line-height: 1.8;">Faster delivery via UPS 2nd Day Air.</p>
<p style="color: #999; font-size: 14px;"><strong>Cost:</strong> $12.99 flat rate</p>
</div>
<div style="margin-bottom: 30px;">
<h3 style="color: #667eea; font-size: 18px;">Overnight Shipping</h3>
<p style="color: #666; line-height: 1.8;">Order by 2 PM EST for next business day delivery.</p>
<p style="color: #999; font-size: 14px;"><strong>Cost:</strong> $24.99 flat rate</p>
</div>
<h2 style="color: #333; margin-bottom: 20px; margin-top: 40px;"><i class="bi bi-globe" style="color: #667eea;"></i> International Shipping</h2>
<p style="color: #666; line-height: 1.8;">We ship to over 50 countries worldwide! International orders typically take 7-14 business days. Customs fees are the responsibility of the customer.</p>
</div>
</div>
</section>
<!-- Shipping Content -->
<section class="section">
<div class="container">
<div class="policy-container" id="shippingContainer">
<!-- Content will be loaded dynamically from the database -->
<div
class="loading-placeholder"
style="text-align: center; padding: 2rem"
>
<i
class="bi bi-hourglass-split"
style="font-size: 2rem; animation: spin 1s linear infinite"
></i>
<p>Loading shipping information...</p>
</div>
</div>
</div>
</section>
</main>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">Sky Art Shop</h3>
<p class="footer-text">
Your destination for unique art pieces and creative supplies.
<div class="footer-about">
<div class="footer-brand">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop"
/>
<span>Sky Art Shop</span>
</div>
<p>
Your one-stop shop for scrapbooking, journaling, cardmaking, and
collaging stationery. Quality products for all your creative
needs.
</p>
<div class="social-links">
<div class="footer-social">
<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>
<a href="#" class="social-link"
><i class="bi bi-instagram"></i
></a>
<a href="#" class="social-link"
><i class="bi bi-pinterest"></i
></a>
<a href="#" class="social-link"><i class="bi bi-youtube"></i></a>
</div>
</div>
<div class="footer-col">
<h4 class="footer-heading">Shop</h4>
<div class="footer-column">
<h4>Quick Links</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="/home">Home</a></li>
<li><a href="/shop">Shop</a></li>
<li><a href="/portfolio">Portfolio</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/about">About Us</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<div class="footer-col">
<h4 class="footer-heading">Customer Service</h4>
<div class="footer-column">
<h4>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="/shipping-info">Shipping Info</a></li>
<li><a href="/returns">Returns & Refunds</a></li>
<li><a href="/privacy">Privacy Policy</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Contact Us</h4>
<ul class="footer-links">
<li><i class="bi bi-envelope"></i> hello@skyartshop.com</li>
<li><i class="bi bi-telephone"></i> (555) 123-4567</li>
<li><i class="bi bi-geo-alt"></i> 123 Creative Lane</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 Sky Art Shop. All rights reserved.</p>
<p>&copy; 2026 Sky Art Shop. All rights reserved.</p>
<p>
Made with
<i class="bi bi-heart-fill" style="color: var(--primary-pink)"></i>
for creative souls
</p>
</div>
</div>
</footer>
<!-- Core Scripts (standardized order) -->
<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 src="/assets/js/navigation.js"></script>
<!-- Cart Drawer -->
<div class="cart-overlay"></div>
<div class="cart-drawer">
<div class="cart-header">
<h3>Shopping Cart</h3>
<button class="cart-close"><i class="bi bi-x"></i></button>
</div>
<div class="cart-items">
<!-- Cart items rendered via JavaScript -->
</div>
<div class="cart-footer">
<div class="cart-total">
<span>Total:</span>
<span class="cart-total-amount">$0.00</span>
</div>
<a href="/checkout" class="btn btn-primary cart-checkout-btn"
>Checkout</a
>
</div>
</div>
<!-- Wishlist Drawer -->
<div class="wishlist-overlay"></div>
<div class="wishlist-drawer">
<div class="wishlist-header">
<h3><i class="bi bi-heart"></i> My Wishlist</h3>
<button class="wishlist-close"><i class="bi bi-x"></i></button>
</div>
<div class="wishlist-items"></div>
<div class="wishlist-footer">
<a href="/shop" class="btn btn-outline">Continue Shopping</a>
</div>
</div>
<!-- Scripts -->
<script src="/assets/js/modern-theme.js?v=20260118c"></script>
<script src="/assets/js/customer-auth.js"></script>
<script src="/assets/js/accessibility.js"></script>
<script>
// Load shipping page data from API
async function loadShippingPageData() {
try {
const response = await fetch("/api/pages/shipping-info");
if (!response.ok) {
console.log("Shipping page not found in database, using defaults");
renderDefaultShippingContent();
return;
}
const data = await response.json();
const pageData = data.pagedata || {};
// Update header if data exists
if (pageData.header) {
if (pageData.header.title) {
document.getElementById("shippingHeaderTitle").innerHTML =
'<i class="bi bi-truck"></i> ' + pageData.header.title;
}
if (pageData.header.subtitle) {
document.getElementById("shippingHeaderSubtitle").textContent =
pageData.header.subtitle;
}
}
// Render sections
const container = document.getElementById("shippingContainer");
if (pageData.sections && pageData.sections.length > 0) {
container.innerHTML = pageData.sections
.map((section) => {
let sectionHtml = "";
if (section.title) {
sectionHtml += `<h2>${escapeHtml(section.title)}</h2>`;
}
if (section.content) {
// Split content by newlines and render as paragraphs
const paragraphs = section.content
.split("\n")
.filter((p) => p.trim());
paragraphs.forEach((p) => {
sectionHtml += `<p>${escapeHtml(p)}</p>`;
});
}
if (section.listItems && section.listItems.length > 0) {
sectionHtml +=
"<ul>" +
section.listItems
.map((item) => `<li>${escapeHtml(item)}</li>`)
.join("") +
"</ul>";
}
return sectionHtml;
})
.join("");
// Add contact box at the end
container.innerHTML += `
<div class="highlight-box">
<h3 style="margin-top: 0">Shipping Questions?</h3>
<p style="margin-bottom: var(--spacing-md)">
Our team is here to help with any shipping inquiries.
</p>
<div style="display: flex; gap: var(--spacing-md); flex-wrap: wrap">
<a href="/contact" class="btn btn-primary">Contact Us</a>
<a href="mailto:support@skyartshop.com" class="btn btn-outline">Email Support</a>
</div>
</div>
`;
} else {
renderDefaultShippingContent();
}
} catch (error) {
console.error("Error loading shipping page:", error);
renderDefaultShippingContent();
}
}
function renderDefaultShippingContent() {
const container = document.getElementById("shippingContainer");
container.innerHTML = `
<div class="highlight-box">
<p style="margin: 0; font-weight: 600; font-size: 1.1rem">
<i class="bi bi-gift"></i> Free standard shipping on orders over $50!
</p>
</div>
<h2>Domestic Shipping Options</h2>
<div class="shipping-options">
<div class="shipping-card">
<div class="shipping-icon"><i class="bi bi-truck"></i></div>
<h3>Standard Shipping</h3>
<p class="price">$5.99</p>
<p style="font-size: 0.9rem; color: var(--text-secondary)">5-7 business days</p>
<p style="font-size: 0.85rem; color: var(--text-light); margin-top: var(--spacing-sm);">FREE on orders $50+</p>
</div>
<div class="shipping-card">
<div class="shipping-icon"><i class="bi bi-lightning-charge"></i></div>
<h3>Express Shipping</h3>
<p class="price">$12.99</p>
<p style="font-size: 0.9rem; color: var(--text-secondary)">2-3 business days</p>
<p style="font-size: 0.85rem; color: var(--text-light); margin-top: var(--spacing-sm);">Guaranteed delivery</p>
</div>
<div class="shipping-card">
<div class="shipping-icon"><i class="bi bi-airplane"></i></div>
<h3>Overnight Shipping</h3>
<p class="price">$24.99</p>
<p style="font-size: 0.9rem; color: var(--text-secondary)">Next business day</p>
<p style="font-size: 0.85rem; color: var(--text-light); margin-top: var(--spacing-sm);">Order before 2 PM EST</p>
</div>
</div>
<h2>Processing Time</h2>
<p>All orders are processed within 1-2 business days (excluding weekends and holidays). You will receive a shipping confirmation email with tracking information once your order ships.</p>
<ul>
<li><strong>In-Stock Items:</strong> Ship within 24 hours</li>
<li><strong>Custom Orders:</strong> 3-5 business days processing time</li>
<li><strong>Pre-Orders:</strong> Ship on or before the release date</li>
</ul>
<h2>International Shipping</h2>
<p>We ship to over 50 countries worldwide! International shipping rates and delivery times vary by destination.</p>
<div class="highlight-box">
<h3 style="margin-top: 0">Shipping Questions?</h3>
<p style="margin-bottom: var(--spacing-md)">Our team is here to help with any shipping inquiries.</p>
<div style="display: flex; gap: var(--spacing-md); flex-wrap: wrap">
<a href="/contact" class="btn btn-primary">Contact Us</a>
<a href="mailto:support@skyartshop.com" class="btn btn-outline">Email Support</a>
</div>
</div>
`;
}
function escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
// Load on page ready
document.addEventListener("DOMContentLoaded", loadShippingPageData);
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

741
website/public/signin.html Normal file
View File

@@ -0,0 +1,741 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sign In | Sky Art Shop</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
/>
<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=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--color-bg-main: #ffebeb;
--color-bg-secondary: #ffd0d0;
--color-bg-promotion: #f6ccde;
--color-accent: #fcb1d8;
--color-text-main: #202023;
}
body {
font-family: "Poppins", -apple-system, BlinkMacSystemFont, sans-serif;
min-height: 100vh;
background: linear-gradient(
135deg,
#ffebeb 0%,
#ffd0d0 50%,
#f6ccde 100%
);
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
position: relative;
}
/* Decorative elements */
.bg-decoration {
position: fixed;
border-radius: 50%;
filter: blur(60px);
opacity: 0.5;
z-index: 0;
}
.bg-decoration.one {
width: 400px;
height: 400px;
background: #fcb1d8;
top: -100px;
left: -100px;
}
.bg-decoration.two {
width: 300px;
height: 300px;
background: #f6ccde;
bottom: -50px;
right: -50px;
}
.bg-decoration.three {
width: 200px;
height: 200px;
background: #ffd0d0;
top: 50%;
right: 20%;
}
.auth-container {
width: 100%;
max-width: 450px;
position: relative;
z-index: 1;
}
/* Logo Section */
.logo-section {
text-align: center;
margin-bottom: 32px;
}
.logo-wrapper {
width: 120px;
height: 120px;
margin: 0 auto 20px;
background: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10px 40px rgba(252, 177, 216, 0.4),
0 0 0 8px rgba(255, 255, 255, 0.5);
overflow: hidden;
}
.logo-wrapper img {
width: 90px;
height: 90px;
object-fit: contain;
}
.logo-section h1 {
font-size: 1.75rem;
font-weight: 700;
color: var(--color-text-main);
margin-bottom: 8px;
}
.logo-section p {
color: #666;
font-size: 0.95rem;
}
/* Form Card */
.form-card {
background: white;
border-radius: 24px;
padding: 40px 36px;
box-shadow: 0 20px 60px rgba(252, 177, 216, 0.25),
0 8px 20px rgba(0, 0, 0, 0.05);
}
/* Form Fields */
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
font-size: 0.875rem;
font-weight: 600;
color: var(--color-text-main);
margin-bottom: 8px;
}
.input-field {
position: relative;
}
.input-field i.field-icon {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
color: #999;
font-size: 1.1rem;
transition: color 0.2s;
}
.input-field input {
width: 100%;
padding: 15px 48px 15px 48px;
border: 2px solid #f0f0f0;
border-radius: 14px;
font-size: 1rem;
font-family: inherit;
transition: all 0.2s ease;
background: #fafafa;
}
.input-field input:focus {
outline: none;
border-color: var(--color-accent);
background: white;
box-shadow: 0 0 0 4px rgba(252, 177, 216, 0.15);
}
.input-field input:focus + i.field-icon,
.input-field input:focus ~ i.field-icon {
color: var(--color-accent);
}
.input-field .toggle-password {
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: #999;
cursor: pointer;
padding: 4px;
transition: color 0.2s;
}
.input-field .toggle-password:hover {
color: #666;
}
/* Options Row */
.options-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 12px;
}
.remember-check {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
}
.remember-check input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: var(--color-accent);
cursor: pointer;
}
.remember-check span {
font-size: 0.9rem;
color: #555;
}
.forgot-link {
font-size: 0.9rem;
color: #e85a9c;
text-decoration: none;
font-weight: 600;
transition: color 0.2s;
}
.forgot-link:hover {
color: #d14485;
}
/* Submit Button */
.btn-signin {
width: 100%;
padding: 16px;
background: linear-gradient(135deg, #fcb1d8 0%, #f6ccde 100%);
color: var(--color-text-main);
border: none;
border-radius: 14px;
font-size: 1rem;
font-weight: 600;
font-family: inherit;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
position: relative;
overflow: hidden;
}
.btn-signin::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.4),
transparent
);
transition: left 0.5s;
}
.btn-signin:hover::before {
left: 100%;
}
.btn-signin:hover {
transform: translateY(-2px);
box-shadow: 0 12px 28px rgba(252, 177, 216, 0.5);
}
.btn-signin:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
/* Spinner */
.spinner {
width: 20px;
height: 20px;
border: 2px solid rgba(32, 32, 35, 0.2);
border-top-color: var(--color-text-main);
border-radius: 50%;
animation: spin 0.8s linear infinite;
display: none;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Divider */
.divider {
display: flex;
align-items: center;
margin: 28px 0;
color: #999;
font-size: 0.85rem;
}
.divider::before,
.divider::after {
content: "";
flex: 1;
height: 1px;
background: linear-gradient(90deg, transparent, #e0e0e0, transparent);
}
.divider span {
padding: 0 16px;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 500;
}
/* Social Buttons */
.social-btns {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.social-btn {
padding: 14px 12px;
border: 2px solid #f0f0f0;
border-radius: 12px;
background: white;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
}
.social-btn:hover {
border-color: var(--color-accent);
background: #fff9fc;
transform: translateY(-1px);
}
.social-btn.google {
color: #ea4335;
}
.social-btn.facebook {
color: #1877f2;
}
.social-btn.apple {
color: #000000;
}
/* Footer Link */
.form-footer {
text-align: center;
margin-top: 28px;
}
.form-footer p {
color: #666;
font-size: 0.95rem;
}
.form-footer a {
color: #e85a9c;
text-decoration: none;
font-weight: 600;
margin-left: 4px;
transition: color 0.2s;
}
.form-footer a:hover {
color: #d14485;
}
/* Toast Notification */
.toast {
position: fixed;
top: 24px;
right: 24px;
padding: 16px 24px;
border-radius: 12px;
color: white;
font-weight: 500;
font-size: 0.95rem;
display: flex;
align-items: center;
gap: 12px;
transform: translateX(120%);
transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
z-index: 1000;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
.toast.show {
transform: translateX(0);
}
.toast.success {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
}
.toast.error {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
}
.toast.info {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
}
/* Error State */
.input-field.error input {
border-color: #ef4444;
background: #fef2f2;
}
.input-field.error i.field-icon {
color: #ef4444;
}
.error-message {
color: #ef4444;
font-size: 0.8rem;
margin-top: 6px;
display: none;
}
.input-field.error + .error-message {
display: block;
}
/* Back to home link */
.back-home {
text-align: center;
margin-top: 24px;
}
.back-home a {
display: inline-flex;
align-items: center;
gap: 6px;
color: #666;
text-decoration: none;
font-size: 0.9rem;
font-weight: 500;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.2s;
}
.back-home a:hover {
color: var(--color-text-main);
background: rgba(252, 177, 216, 0.2);
}
</style>
</head>
<body>
<!-- Background decorations -->
<div class="bg-decoration one"></div>
<div class="bg-decoration two"></div>
<div class="bg-decoration three"></div>
<!-- Toast Notification -->
<div class="toast" id="toast">
<i class="bi bi-check-circle"></i>
<span id="toastMessage">Message</span>
</div>
<div class="auth-container">
<!-- Logo Section -->
<div class="logo-section">
<div class="logo-wrapper">
<img
src="/uploads/cat-logo-only-1766962993568-201212396.png"
alt="Sky Art Shop"
/>
</div>
<h1>Welcome Back!</h1>
<p>Sign in to continue your creative journey</p>
</div>
<!-- Form Card -->
<div class="form-card">
<form id="signinForm" novalidate>
<div class="form-group">
<label for="email">Email Address</label>
<div class="input-field">
<input
type="email"
id="email"
name="email"
placeholder="you@example.com"
required
/>
<i class="bi bi-envelope field-icon"></i>
</div>
<div class="error-message">Please enter a valid email address</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<div class="input-field">
<input
type="password"
id="password"
name="password"
placeholder="Enter your password"
required
/>
<i class="bi bi-lock field-icon"></i>
<button
type="button"
class="toggle-password"
onclick="togglePassword()"
>
<i class="bi bi-eye" id="toggleIcon"></i>
</button>
</div>
<div class="error-message">Password is required</div>
</div>
<div class="options-row">
<label class="remember-check">
<input type="checkbox" name="remember" id="remember" />
<span>Remember me</span>
</label>
<a
href="#"
class="forgot-link"
onclick="showToast('Password reset coming soon!', 'info'); return false;"
>Forgot password?</a
>
</div>
<button type="submit" class="btn-signin" id="submitBtn">
<i class="bi bi-box-arrow-in-right"></i>
<span id="btnText">Sign In</span>
<div class="spinner" id="spinner"></div>
</button>
</form>
<div class="divider">
<span>or continue with</span>
</div>
<div class="social-btns">
<button
type="button"
class="social-btn google"
onclick="showToast('Google login coming soon!', 'info')"
>
<i class="bi bi-google"></i>
</button>
<button
type="button"
class="social-btn facebook"
onclick="showToast('Facebook login coming soon!', 'info')"
>
<i class="bi bi-facebook"></i>
</button>
<button
type="button"
class="social-btn apple"
onclick="showToast('Apple login coming soon!', 'info')"
>
<i class="bi bi-apple"></i>
</button>
</div>
<div class="form-footer">
<p>Don't have an account?<a href="/signup">Create one</a></p>
</div>
</div>
<div class="back-home">
<a href="/"><i class="bi bi-arrow-left"></i> Back to Shop</a>
</div>
</div>
<script>
// Toggle password visibility
function togglePassword() {
const passwordInput = document.getElementById("password");
const toggleIcon = document.getElementById("toggleIcon");
if (passwordInput.type === "password") {
passwordInput.type = "text";
toggleIcon.classList.remove("bi-eye");
toggleIcon.classList.add("bi-eye-slash");
} else {
passwordInput.type = "password";
toggleIcon.classList.remove("bi-eye-slash");
toggleIcon.classList.add("bi-eye");
}
}
// Toast notification
function showToast(message, type = "success") {
const toast = document.getElementById("toast");
const toastMessage = document.getElementById("toastMessage");
const icon = toast.querySelector("i");
toastMessage.textContent = message;
toast.className = "toast " + type;
// Update icon based on type
icon.className = "bi";
if (type === "success") icon.classList.add("bi-check-circle");
else if (type === "error") icon.classList.add("bi-exclamation-circle");
else if (type === "info") icon.classList.add("bi-info-circle");
toast.classList.add("show");
setTimeout(() => {
toast.classList.remove("show");
}, 4000);
}
// Form submission
document
.getElementById("signinForm")
.addEventListener("submit", async function (e) {
e.preventDefault();
const email = document.getElementById("email").value.trim();
const password = document.getElementById("password").value;
const remember = document.getElementById("remember").checked;
// Clear previous errors
document
.querySelectorAll(".input-field")
.forEach((f) => f.classList.remove("error"));
// Validate
let hasError = false;
if (!email || !email.includes("@")) {
document
.getElementById("email")
.parentElement.classList.add("error");
hasError = true;
}
if (!password) {
document
.getElementById("password")
.parentElement.classList.add("error");
hasError = true;
}
if (hasError) return;
// Show loading state
const submitBtn = document.getElementById("submitBtn");
const btnText = document.getElementById("btnText");
const spinner = document.getElementById("spinner");
submitBtn.disabled = true;
btnText.style.display = "none";
submitBtn.querySelector("i").style.display = "none";
spinner.style.display = "block";
try {
const response = await fetch("/api/customers/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
credentials: "include",
});
const data = await response.json();
if (data.success) {
// Store customer data
localStorage.setItem("customer", JSON.stringify(data.customer));
if (remember) {
localStorage.setItem("rememberCustomer", "true");
}
showToast("Welcome back! Redirecting...", "success");
setTimeout(() => {
window.location.href = "/account";
}, 1200);
} else {
showToast(data.message || "Invalid email or password", "error");
// Reset button
submitBtn.disabled = false;
btnText.style.display = "inline";
submitBtn.querySelector("i").style.display = "inline";
spinner.style.display = "none";
}
} catch (error) {
console.error("Login error:", error);
showToast("Connection error. Please try again.", "error");
// Reset button
submitBtn.disabled = false;
btnText.style.display = "inline";
submitBtn.querySelector("i").style.display = "inline";
spinner.style.display = "none";
}
});
// Check if already logged in
document.addEventListener("DOMContentLoaded", function () {
const customer = localStorage.getItem("customer");
if (customer) {
try {
const parsed = JSON.parse(customer);
if (parsed && parsed.email) {
// Already logged in, redirect to account
window.location.href = "/account";
}
} catch (e) {}
}
});
</script>
</body>
</html>

1243
website/public/signup.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sticky Test - Inline CSS</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
background: #f5f5f5;
}
/* STICKY NAVBAR - This should work */
.sticky-navbar-wrapper {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 1000;
background: #FFD0D0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.navbar-content {
padding: 20px;
display: flex;
align-items: center;
justify-content: space-between;
max-width: 1200px;
margin: 0 auto;
}
.navbar-title {
font-size: 24px;
font-weight: bold;
color: #202023;
}
.navbar-status {
background: #4CAF50;
color: white;
padding: 8px 16px;
border-radius: 4px;
font-size: 14px;
}
.content {
padding: 40px 20px;
max-width: 1200px;
margin: 0 auto;
}
.section {
background: white;
padding: 40px;
margin-bottom: 20px;
border-radius: 8px;
min-height: 400px;
}
.section h2 {
color: #202023;
margin-bottom: 20px;
}
.scroll-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #FFD0D0;
padding: 15px 25px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
font-weight: bold;
}
</style>
</head>
<body>
<!-- THIS IS THE STICKY NAVBAR -->
<div class="sticky-navbar-wrapper">
<div class="navbar-content">
<div class="navbar-title">🔍 STICKY NAVBAR TEST</div>
<div class="navbar-status">I should stay at top when scrolling ↓</div>
</div>
</div>
<!-- CONTENT TO SCROLL -->
<div class="content">
<div class="section">
<h2>Section 1 - Start Scrolling</h2>
<p style="font-size: 18px; line-height: 1.8;">
If the pink navbar above stays visible while you scroll, then position: sticky is working correctly.
<br><br>
<strong>What to look for:</strong>
<br>• The pink navbar should remain at the top of the window
<br>• It should NOT scroll away with the content
<br>• The green badge should always be visible
</p>
</div>
<div class="section">
<h2>Section 2 - Keep Scrolling</h2>
<p style="font-size: 18px;">Continue scrolling down to test the sticky behavior...</p>
</div>
<div class="section">
<h2>Section 3 - More Scrolling</h2>
<p style="font-size: 18px;">The navbar should still be at the top...</p>
</div>
<div class="section">
<h2>Section 4 - Almost There</h2>
<p style="font-size: 18px;">Keep going...</p>
</div>
<div class="section">
<h2>Section 5 - Final Test</h2>
<p style="font-size: 18px;">
If you can still see the pink navbar at the top, it's working!
<br><br>
If it scrolled away, there's a browser or CSS issue preventing sticky positioning.
</p>
</div>
</div>
<div class="scroll-indicator">
Scroll to test ↑↓
</div>
</body>
</html>

View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<title>Sticky Navbar Test</title>
<link rel="stylesheet" href="/assets/css/navbar.css?v=1768450104" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1768450104" />
<style>
body { margin: 0; padding: 0; }
.test-content { height: 2000px; padding: 20px; }
</style>
</head>
<body>
<div class="sticky-banner-wrapper">
<nav class="modern-navbar">
<div class="navbar-wrapper" style="padding: 20px;">
<h2 style="margin: 0; color: #202023;">TEST: Navbar Should Stick When Scrolling</h2>
</div>
</nav>
</div>
<div class="test-content">
<h1>Scroll down to test sticky navbar</h1>
<p>If the navbar stays at the top, CSS is working.</p>
<p>If the navbar scrolls away, there's still an issue.</p>
<p style="margin-top: 500px;">Keep scrolling...</p>
<p style="margin-top: 500px;">Almost there...</p>
<p style="margin-top: 500px;">End of page</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sticky Test</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #ffebeb;
min-height: 200vh; /* Make it scrollable */
}
.sticky-banner-wrapper {
position: sticky;
top: 0;
z-index: 1000;
background: #ffd0d0;
}
.test-navbar {
padding: 20px;
background: #ffd0d0;
font-family: Arial, sans-serif;
}
.content {
padding: 50px;
font-size: 20px;
}
</style>
</head>
<body>
<div class="sticky-banner-wrapper">
<div class="test-navbar">
<h2>TEST NAVBAR - Should stay at top when scrolling</h2>
</div>
</div>
<div class="content">
<h1>Scroll down to test sticky behavior</h1>
<p>Content line 1</p>
<p>Content line 2</p>
<p>Content line 3</p>
<p>Content line 4</p>
<p>Content line 5</p>
<p>Content line 6</p>
<p>Content line 7</p>
<p>Content line 8</p>
<p>Content line 9</p>
<p>Content line 10</p>
<p>Content line 11</p>
<p>Content line 12</p>
<p>Content line 13</p>
<p>Content line 14</p>
<p>Content line 15</p>
<p>Content line 16</p>
<p>Content line 17</p>
<p>Content line 18</p>
<p>Content line 19</p>
<p>Content line 20</p>
</div>
</body>
</html>