Add website file management workflow and deployment script

This commit is contained in:
Local Server
2025-12-13 22:59:42 -06:00
parent f3c1157d7e
commit dce6460994
35 changed files with 8858 additions and 0 deletions

View File

@@ -0,0 +1,334 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Admin Dashboard - Sky Art Shop</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
/>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
background-color: #f8f9fa;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 250px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
color: white;
overflow-y: auto;
}
.sidebar-brand {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 30px;
text-align: center;
}
.sidebar-menu {
list-style: none;
padding: 0;
}
.sidebar-menu li {
margin-bottom: 10px;
}
.sidebar-menu a {
color: white;
text-decoration: none;
display: flex;
align-items: center;
padding: 12px 15px;
border-radius: 8px;
transition: background 0.3s;
}
.sidebar-menu a:hover,
.sidebar-menu a.active {
background: rgba(255, 255, 255, 0.2);
}
.sidebar-menu i {
margin-right: 10px;
font-size: 1.2rem;
}
.main-content {
margin-left: 250px;
padding: 30px;
}
.top-bar {
background: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 30px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.stat-card {
background: white;
border-radius: 10px;
padding: 25px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
cursor: pointer;
text-decoration: none;
color: inherit;
display: block;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.stat-card h6 {
color: #6c757d;
margin-bottom: 10px;
}
.stat-card h2 {
color: #2c3e50;
margin: 0;
}
.stat-link {
color: #667eea;
font-size: 0.9rem;
margin-top: 10px;
display: inline-block;
}
.action-card {
background: white;
border-radius: 10px;
padding: 30px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
text-align: center;
transition: transform 0.2s;
cursor: pointer;
}
.action-card:hover {
transform: translateY(-5px);
}
.action-card i {
font-size: 3rem;
margin-bottom: 15px;
}
.btn-view-site {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 10px 25px;
border-radius: 8px;
text-decoration: none;
display: inline-block;
}
.btn-logout {
background: #dc3545;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
}
</style>
</head>
<body>
<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-brand">🛍️ Sky Art Shop</div>
<ul class="sidebar-menu">
<li>
<a href="/admin/dashboard.html" class="active"
><i class="bi bi-speedometer2"></i> Dashboard</a
>
</li>
<li>
<a href="/admin/homepage.html"
><i class="bi bi-house"></i> Homepage Editor</a
>
</li>
<li>
<a href="/admin/products.html"><i class="bi bi-box"></i> Products</a>
</li>
<li>
<a href="/admin/portfolio.html"
><i class="bi bi-easel"></i> Portfolio</a
>
</li>
<li>
<a href="/admin/blog.html"><i class="bi bi-newspaper"></i> Blog</a>
</li>
<li>
<a href="/admin/pages.html"><i class="bi bi-file-text"></i> Pages</a>
</li>
<li>
<a href="/admin/menu.html"><i class="bi bi-list"></i> Menu</a>
</li>
<li>
<a href="/admin/settings.html"><i class="bi bi-gear"></i> Settings</a>
</li>
<li>
<a href="/admin/users.html"><i class="bi bi-people"></i> Users</a>
</li>
</ul>
</div>
<!-- Main Content -->
<div class="main-content">
<!-- Top Bar -->
<div class="top-bar">
<div>
<h3>Welcome, <span id="userName">Admin</span></h3>
<p class="mb-0 text-muted">Manage your online shop</p>
</div>
<div>
<a href="/index.html" target="_blank" class="btn-view-site me-2"
><i class="bi bi-eye"></i> View Site</a
>
<button class="btn-logout" onclick="logout()">
<i class="bi bi-box-arrow-right"></i> Logout
</button>
</div>
</div>
<!-- Stats Cards -->
<div class="row">
<div class="col-md-3 mb-4">
<a href="/admin/products.html" class="stat-card">
<h6>Total Products</h6>
<h2 id="productCount">-</h2>
<span class="stat-link">Manage →</span>
</a>
</div>
<div class="col-md-3 mb-4">
<a href="/admin/portfolio.html" class="stat-card">
<h6>Portfolio Projects</h6>
<h2 id="projectCount">-</h2>
<span class="stat-link">Manage →</span>
</a>
</div>
<div class="col-md-3 mb-4">
<a href="/admin/blog.html" class="stat-card">
<h6>Blog Posts</h6>
<h2 id="blogCount">-</h2>
<span class="stat-link">Manage →</span>
</a>
</div>
<div class="col-md-3 mb-4">
<a href="/admin/pages.html" class="stat-card">
<h6>Custom Pages</h6>
<h2 id="pageCount">-</h2>
<span class="stat-link">Manage →</span>
</a>
</div>
</div>
<!-- Quick Actions -->
<h4 class="mt-5 mb-4">Quick Actions</h4>
<div class="row">
<div class="col-md-3 mb-4">
<a href="/admin/homepage.html" class="text-decoration-none">
<div class="action-card">
<i class="bi bi-house-fill text-success"></i>
<h6>Homepage Editor</h6>
</div>
</a>
</div>
<div class="col-md-3 mb-4">
<a
href="/admin/products.html?action=create"
class="text-decoration-none"
>
<div class="action-card">
<i class="bi bi-plus-circle text-primary"></i>
<h6>Add New Product</h6>
</div>
</a>
</div>
<div class="col-md-3 mb-4">
<a href="/admin/blog.html?action=create" class="text-decoration-none">
<div class="action-card">
<i class="bi bi-plus-circle text-info"></i>
<h6>Create Blog Post</h6>
</div>
</a>
</div>
<div class="col-md-3 mb-4">
<a
href="/admin/portfolio.html?action=create"
class="text-decoration-none"
>
<div class="action-card">
<i class="bi bi-plus-circle text-warning"></i>
<h6>Add Portfolio Project</h6>
</div>
</a>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Check authentication
async function checkAuth() {
try {
const response = await fetch("/api/admin/session");
credentials: "include",
if (!response.ok) {
window.location.href = "/admin/login.html";
return;
}
const data = await response.json();
if (!data.authenticated) {
window.location.href = "/admin/login.html";
return;
}
document.getElementById("userName").textContent =
data.user.name || data.user.email;
loadDashboardStats();
} catch (error) {
window.location.href = "/admin/login.html";
}
}
// Load dashboard statistics
async function loadDashboardStats() {
try {
const response = await fetch("/api/admin/dashboard/stats");
credentials: "include",
const data = await response.json();
if (data.success) {
document.getElementById("productCount").textContent =
data.stats.products;
document.getElementById("projectCount").textContent =
data.stats.projects;
document.getElementById("blogCount").textContent = data.stats.blog;
document.getElementById("pageCount").textContent = data.stats.pages;
}
} catch (error) {
console.error("Failed to load stats:", error);
}
}
// Logout function
async function logout() {
try {
const response = await fetch("/api/admin/logout", { method: "POST" });
credentials: "include",
if (response.ok) {
window.location.href = "/admin/login.html";
}
} catch (error) {
console.error("Logout failed:", error);
}
}
// Initialize
checkAuth();
</script>
</body>
</html>

171
website/admin/login.html Normal file
View File

@@ -0,0 +1,171 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Admin Login - Sky Art Shop</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
}
.login-card {
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 40px;
max-width: 400px;
width: 100%;
}
.login-header {
text-align: center;
margin-bottom: 30px;
}
.login-header h1 {
color: #2c3e50;
font-size: 28px;
margin-bottom: 10px;
}
.login-header p {
color: #7f8c8d;
margin: 0;
}
.form-control {
border-radius: 8px;
padding: 12px;
border: 2px solid #e0e0e0;
}
.form-control:focus {
border-color: #667eea;
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
}
.btn-login {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
padding: 12px;
color: white;
font-weight: 600;
width: 100%;
transition: transform 0.2s;
}
.btn-login:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn-login:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.alert {
border-radius: 8px;
display: none;
}
</style>
</head>
<body>
<div class="login-card">
<div class="login-header">
<h1>🛍️ Sky Art Shop</h1>
<p>Admin Panel Login</p>
</div>
<div class="alert alert-danger" role="alert" id="errorAlert"></div>
<form id="loginForm">
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input
type="email"
class="form-control"
id="email"
name="email"
required
placeholder="admin@example.com"
autocomplete="username"
/>
</div>
<div class="mb-4">
<label for="password" class="form-label">Password</label>
<input
type="password"
class="form-control"
id="password"
name="password"
required
placeholder="Enter your password"
autocomplete="current-password"
/>
</div>
<button type="submit" class="btn btn-login" id="loginBtn">Sign In</button>
</form>
<div class="text-center mt-4">
<a
href="/home.html"
class="text-decoration-none"
style="color: #667eea"
>← Back to Website</a
>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document
.getElementById("loginForm")
.addEventListener("submit", async function (e) {
e.preventDefault();
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
const errorAlert = document.getElementById("errorAlert");
const loginBtn = document.getElementById("loginBtn");
// Disable button during login
loginBtn.disabled = true;
loginBtn.textContent = "Signing in...";
errorAlert.style.display = "none";
try {
const response = await fetch("/api/admin/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({ email, password }),
});
const data = await response.json();
if (response.ok && data.success) {
// Login successful - redirect to dashboard
window.location.href = "/admin/dashboard.html";
} else {
// Show error
errorAlert.textContent = data.message || "Invalid credentials";
errorAlert.style.display = "block";
loginBtn.disabled = false;
loginBtn.textContent = "Sign In";
}
} catch (error) {
console.error("Login error:", error);
errorAlert.textContent = "Login failed. Please try again.";
errorAlert.style.display = "block";
loginBtn.disabled = false;
loginBtn.textContent = "Sign In";
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,153 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Admin Login - Sky Art Shop</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
}
.login-card {
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 40px;
max-width: 400px;
width: 100%;
}
.login-header {
text-align: center;
margin-bottom: 30px;
}
.login-header h1 {
color: #2c3e50;
font-size: 28px;
margin-bottom: 10px;
}
.login-header p {
color: #7f8c8d;
margin: 0;
}
.form-control {
border-radius: 8px;
padding: 12px;
border: 2px solid #e0e0e0;
}
.form-control:focus {
border-color: #667eea;
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
}
.btn-login {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
padding: 12px;
color: white;
font-weight: 600;
width: 100%;
transition: transform 0.2s;
}
.btn-login:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.alert {
border-radius: 8px;
display: none;
}
</style>
</head>
<body>
<div class="login-card">
<div class="login-header">
<h1>🛍️ Sky Art Shop</h1>
<p>Admin Panel Login</p>
</div>
<div class="alert alert-danger" role="alert" id="errorAlert"></div>
<form id="loginForm">
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input
type="email"
class="form-control"
id="email"
name="email"
required
placeholder="admin@skyartshop.com"
/>
</div>
<div class="mb-4">
<label for="password" class="form-label">Password</label>
<input
type="password"
class="form-control"
id="password"
name="password"
required
placeholder="Enter your password"
/>
</div>
<button type="submit" class="btn btn-login">Sign In</button>
</form>
<div class="text-center mt-4">
<a
href="/index.html"
class="text-decoration-none"
style="color: #667eea"
>← Back to Website</a
>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document
.getElementById("loginForm")
.addEventListener("submit", async function (e) {
e.preventDefault();
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
const errorAlert = document.getElementById("errorAlert");
try {
const response = await fetch("/api/admin/login", {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({ email, password }),
});
const data = await response.json();
if (response.ok && data.success) {
window.location.href = "/admin/dashboard.html";
} else {
errorAlert.textContent = data.message || "Invalid credentials";
errorAlert.style.display = "block";
}
} catch (error) {
errorAlert.textContent = "Login failed. Please try again.";
errorAlert.style.display = "block";
}
});
</script>
</body>
</html>

647
website/admin/users.html Normal file
View File

@@ -0,0 +1,647 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>User Management - Sky Art Shop Admin</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" />
<style>
body {
font-family: 'Roboto', sans-serif;
background: #f5f7fa;
margin: 0;
padding: 0;
}
.admin-header {
background: white;
padding: 16px 32px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.admin-header h1 {
margin: 0;
font-size: 24px;
color: #1a1a1a;
}
.container {
max-width: 1400px;
margin: 32px auto;
padding: 0 32px;
}
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: #6b46c1;
color: white;
}
.btn-primary:hover {
background: #5936a3;
}
.btn-secondary {
background: #e5e7eb;
color: #374151;
}
.btn-secondary:hover {
background: #d1d5db;
}
.btn-danger {
background: #dc2626;
color: white;
}
.btn-danger:hover {
background: #b91c1c;
}
.btn-success {
background: #10b981;
color: white;
}
.btn-success:hover {
background: #059669;
}
.users-table {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
thead {
background: #f9fafb;
}
th {
padding: 16px;
text-align: left;
font-weight: 600;
color: #6b7280;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
td {
padding: 16px;
border-top: 1px solid #e5e7eb;
color: #374151;
}
tbody tr:hover {
background: #f9fafb;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-active {
background: #d1fae5;
color: #065f46;
}
.status-inactive {
background: #fee2e2;
color: #991b1b;
}
.role-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
background: #e0e7ff;
color: #3730a3;
}
.btn-group {
display: flex;
gap: 8px;
}
.btn-icon {
width: 32px;
height: 32px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
font-size: 16px;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal.active {
display: flex;
}
.modal-content {
background: white;
border-radius: 12px;
padding: 32px;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.modal-header h2 {
margin: 0;
font-size: 24px;
color: #1a1a1a;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #374151;
}
.form-group input,
.form-group select {
width: 100%;
padding: 10px 14px;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 14px;
font-family: 'Roboto', sans-serif;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #6b46c1;
box-shadow: 0 0 0 3px rgba(107, 70, 193, 0.1);
}
.checkbox-group {
display: flex;
align-items: center;
gap: 8px;
}
.checkbox-group input[type="checkbox"] {
width: auto;
}
.alert {
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 20px;
display: none;
}
.alert.active {
display: block;
}
.alert-success {
background: #d1fae5;
color: #065f46;
}
.alert-error {
background: #fee2e2;
color: #991b1b;
}
</style>
</head>
<body>
<div class="admin-header">
<h1><i class="bi bi-people"></i> User Management</h1>
<a href="/admin/dashboard.html" class="btn btn-secondary">
<i class="bi bi-arrow-left"></i> Back to Dashboard
</a>
</div>
<div class="container">
<div class="action-bar">
<h2 style="margin: 0; font-size: 20px; color: #374151;">All Users</h2>
<button class="btn btn-primary" onclick="openCreateUserModal()">
<i class="bi bi-person-plus"></i> Create New User
</button>
</div>
<div id="alert" class="alert"></div>
<div class="users-table">
<table>
<thead>
<tr>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Status</th>
<th>Last Login</th>
<th>Password</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="usersTableBody">
<tr>
<td colspan="7" style="text-align: center; padding: 40px;">
Loading users...
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Create/Edit User Modal -->
<div id="userModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2 id="modalTitle">Create New User</h2>
<button class="btn-icon btn-secondary" onclick="closeUserModal()">
<i class="bi bi-x-lg"></i>
</button>
</div>
<form id="userForm">
<input type="hidden" id="userId" />
<div class="form-group">
<label for="username">Username *</label>
<input type="text" id="username" required />
</div>
<div class="form-group">
<label for="email">Email *</label>
<input type="email" id="email" required />
</div>
<div class="form-group" id="passwordGroup">
<label for="password">Password *</label>
<input type="password" id="password" minlength="6" />
<small style="color: #6b7280;">Minimum 6 characters</small>
</div>
<div class="form-group">
<label for="role_id">Role *</label>
<select id="role_id" required>
<option value="">Select a role...</option>
</select>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="password_never_expires" />
<label for="password_never_expires" style="margin: 0;">Password never expires</label>
</div>
<small style="color: #6b7280; display: block; margin-top: 4px;">
If unchecked, password will expire after 90 days
</small>
</div>
<div style="display: flex; gap: 12px; margin-top: 24px;">
<button type="submit" class="btn btn-primary" style="flex: 1;">
<i class="bi bi-check-lg"></i> Save User
</button>
<button type="button" class="btn btn-secondary" onclick="closeUserModal()">
Cancel
</button>
</div>
</form>
</div>
</div>
<!-- Reset Password Modal -->
<div id="resetPasswordModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Reset Password</h2>
<button class="btn-icon btn-secondary" onclick="closeResetPasswordModal()">
<i class="bi bi-x-lg"></i>
</button>
</div>
<form id="resetPasswordForm">
<input type="hidden" id="resetUserId" />
<p style="color: #6b7280; margin-bottom: 20px;">
Enter a new password for <strong id="resetUsername"></strong>
</p>
<div class="form-group">
<label for="new_password">New Password *</label>
<input type="password" id="new_password" minlength="6" required />
<small style="color: #6b7280;">Minimum 6 characters</small>
</div>
<div class="form-group">
<label for="confirm_password">Confirm Password *</label>
<input type="password" id="confirm_password" minlength="6" required />
</div>
<div style="display: flex; gap: 12px; margin-top: 24px;">
<button type="submit" class="btn btn-primary" style="flex: 1;">
<i class="bi bi-key"></i> Reset Password
</button>
<button type="button" class="btn btn-secondary" onclick="closeResetPasswordModal()">
Cancel
</button>
</div>
</form>
</div>
</div>
<script>
let users = [];
let roles = [];
let editingUserId = null;
// Check authentication
async function checkAuth() {
try {
const response = await fetch('/api/admin/session');
const data = await response.json();
if (!data.authenticated) {
window.location.href = '/admin/login.html';
}
} catch (error) {
window.location.href = '/admin/login.html';
}
}
// Load roles
async function loadRoles() {
try {
const response = await fetch('/api/admin/users/roles');
const data = await response.json();
if (data.success) {
roles = data.roles;
const select = document.getElementById('role_id');
select.innerHTML = '<option value="">Select a role...</option>' +
roles.map(role => \`<option value="\${role.id}">\${role.name} - \${role.description}</option>\`).join('');
}
} catch (error) {
console.error('Error loading roles:', error);
}
}
// Load users
async function loadUsers() {
try {
const response = await fetch('/api/admin/users');
const data = await response.json();
if (data.success) {
users = data.users;
renderUsers();
}
} catch (error) {
console.error('Error loading users:', error);
showAlert('Error loading users', 'error');
}
}
// Render users table
function renderUsers() {
const tbody = document.getElementById('usersTableBody');
if (users.length === 0) {
tbody.innerHTML = '<tr><td colspan="7" style="text-align: center; padding: 40px; color: #9ca3af;">No users found</td></tr>';
return;
}
tbody.innerHTML = users.map(user => \`
<tr>
<td style="font-weight: 500;">\${user.username}</td>
<td>\${user.email}</td>
<td><span class="role-badge">\${user.role_name || 'Unknown'}</span></td>
<td>
<span class="status-badge \${user.isactive ? 'status-active' : 'status-inactive'}">
\${user.isactive ? 'Active' : 'Inactive'}
</span>
</td>
<td>\${user.last_login ? new Date(user.last_login).toLocaleDateString() : 'Never'}</td>
<td>
\${user.password_never_expires ?
'<span style="color: #10b981;">Never expires</span>' :
'<span style="color: #f59e0b;">Expires in 90 days</span>'
}
</td>
<td>
<div class="btn-group">
<button class="btn-icon btn-secondary" onclick="editUser('\${user.id}')" title="Edit">
<i class="bi bi-pencil"></i>
</button>
<button class="btn-icon btn-primary" onclick="resetPassword('\${user.id}', '\${user.username}')" title="Reset Password">
<i class="bi bi-key"></i>
</button>
<button class="btn-icon \${user.isactive ? 'btn-secondary' : 'btn-success'}"
onclick="toggleStatus('\${user.id}')"
title="\${user.isactive ? 'Deactivate' : 'Activate'}">
<i class="bi bi-\${user.isactive ? 'pause' : 'play'}-circle"></i>
</button>
<button class="btn-icon btn-danger" onclick="deleteUser('\${user.id}')" title="Delete">
<i class="bi bi-trash"></i>
</button>
</div>
</td>
</tr>
\`).join('');
}
// Open create user modal
function openCreateUserModal() {
editingUserId = null;
document.getElementById('modalTitle').textContent = 'Create New User';
document.getElementById('userId').value = '';
document.getElementById('userForm').reset();
document.getElementById('passwordGroup').style.display = 'block';
document.getElementById('password').required = true;
document.getElementById('userModal').classList.add('active');
}
// Edit user
function editUser(id) {
const user = users.find(u => u.id === id);
if (!user) return;
editingUserId = id;
document.getElementById('modalTitle').textContent = 'Edit User';
document.getElementById('userId').value = user.id;
document.getElementById('username').value = user.username;
document.getElementById('email').value = user.email;
document.getElementById('role_id').value = user.role_id;
document.getElementById('password_never_expires').checked = user.password_never_expires;
document.getElementById('passwordGroup').style.display = 'none';
document.getElementById('password').required = false;
document.getElementById('userModal').classList.add('active');
}
// Close user modal
function closeUserModal() {
document.getElementById('userModal').classList.remove('active');
document.getElementById('userForm').reset();
editingUserId = null;
}
// Submit user form
document.getElementById('userForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = {
username: document.getElementById('username').value,
email: document.getElementById('email').value,
role_id: document.getElementById('role_id').value,
password_never_expires: document.getElementById('password_never_expires').checked
};
if (!editingUserId) {
formData.password = document.getElementById('password').value;
}
try {
const url = editingUserId ? \`/api/admin/users/\${editingUserId}\` : '/api/admin/users';
const method = editingUserId ? 'PUT' : 'POST';
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
const data = await response.json();
if (data.success) {
showAlert(data.message, 'success');
closeUserModal();
loadUsers();
} else {
showAlert(data.message, 'error');
}
} catch (error) {
console.error('Error saving user:', error);
showAlert('Error saving user', 'error');
}
});
// Reset password
function resetPassword(id, username) {
document.getElementById('resetUserId').value = id;
document.getElementById('resetUsername').textContent = username;
document.getElementById('resetPasswordForm').reset();
document.getElementById('resetPasswordModal').classList.add('active');
}
// Close reset password modal
function closeResetPasswordModal() {
document.getElementById('resetPasswordModal').classList.remove('active');
document.getElementById('resetPasswordForm').reset();
}
// Submit reset password form
document.getElementById('resetPasswordForm').addEventListener('submit', async (e) => {
e.preventDefault();
const newPassword = document.getElementById('new_password').value;
const confirmPassword = document.getElementById('confirm_password').value;
if (newPassword !== confirmPassword) {
showAlert('Passwords do not match', 'error');
return;
}
const userId = document.getElementById('resetUserId').value;
try {
const response = await fetch(\`/api/admin/users/\${userId}/reset-password\`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ new_password: newPassword })
});
const data = await response.json();
if (data.success) {
showAlert(data.message, 'success');
closeResetPasswordModal();
} else {
showAlert(data.message, 'error');
}
} catch (error) {
console.error('Error resetting password:', error);
showAlert('Error resetting password', 'error');
}
});
// Toggle user status
async function toggleStatus(id) {
if (!confirm('Are you sure you want to change this user\'s status?')) return;
try {
const response = await fetch(\`/api/admin/users/\${id}/toggle-status\`, {
method: 'POST'
});
const data = await response.json();
if (data.success) {
showAlert(data.message, 'success');
loadUsers();
} else {
showAlert(data.message, 'error');
}
} catch (error) {
console.error('Error toggling status:', error);
showAlert('Error toggling status', 'error');
}
}
// Delete user
async function deleteUser(id) {
if (!confirm('Are you sure you want to delete this user? This action cannot be undone.')) return;
try {
const response = await fetch(\`/api/admin/users/\${id}\`, {
method: 'DELETE'
});
const data = await response.json();
if (data.success) {
showAlert(data.message, 'success');
loadUsers();
} else {
showAlert(data.message, 'error');
}
} catch (error) {
console.error('Error deleting user:', error);
showAlert('Error deleting user', 'error');
}
}
// Show alert
function showAlert(message, type) {
const alert = document.getElementById('alert');
alert.textContent = message;
alert.className = \`alert alert-\${type} active\`;
setTimeout(() => {
alert.classList.remove('active');
}, 5000);
}
// Initialize
checkAuth();
loadRoles();
loadUsers();
</script>
</body>
</html>

3130
website/assets/css/main.css Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,422 @@
/* Modern Navbar Styles */
.modern-navbar {
position: sticky;
top: 0;
z-index: 1000;
background: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
font-family: 'Roboto', sans-serif;
}
.navbar-wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
height: 72px;
}
/* Logo Section */
.navbar-brand {
flex-shrink: 0;
}
.brand-link {
display: flex;
align-items: center;
gap: 12px;
text-decoration: none;
transition: opacity 0.2s;
}
.brand-link:hover {
opacity: 0.8;
}
.brand-logo {
width: 48px;
height: 48px;
object-fit: contain;
border-radius: 8px;
}
.brand-name {
font-size: 20px;
font-weight: 600;
color: #1a1a1a;
letter-spacing: 0.3px;
white-space: nowrap;
}
/* Main Navigation */
.navbar-menu {
flex: 1;
display: flex;
justify-content: center;
padding: 0 32px;
}
.nav-menu-list {
display: flex;
align-items: center;
gap: 8px;
list-style: none;
margin: 0;
padding: 0;
}
.nav-item {
margin: 0;
}
.nav-link {
display: block;
padding: 10px 20px;
font-size: 15px;
font-weight: 500;
color: #4a4a4a;
text-decoration: none;
border-radius: 6px;
transition: all 0.2s;
letter-spacing: 0.3px;
}
.nav-link:hover,
.nav-link.active {
color: #6b46c1;
background: #f3f0ff;
}
/* Right Actions */
.navbar-actions {
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
.action-item {
position: relative;
}
.action-btn {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border: none;
background: transparent;
color: #4a4a4a;
font-size: 22px;
border-radius: 50%;
cursor: pointer;
transition: all 0.2s;
}
.action-btn:hover {
background: #f5f5f5;
color: #6b46c1;
}
.action-badge {
position: absolute;
top: 6px;
right: 6px;
min-width: 18px;
height: 18px;
padding: 0 5px;
background: #dc2626;
color: white;
font-size: 11px;
font-weight: 600;
border-radius: 9px;
display: none;
align-items: center;
justify-content: center;
}
.action-badge.show {
display: flex;
}
/* Dropdown Styles */
.action-dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
width: 380px;
max-height: 500px;
background: white;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
display: none;
flex-direction: column;
z-index: 1001;
}
.action-dropdown.active {
display: flex;
}
.dropdown-head {
padding: 20px;
border-bottom: 1px solid #e5e5e5;
display: flex;
align-items: center;
justify-content: space-between;
}
.dropdown-head h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #1a1a1a;
}
.dropdown-close {
width: 32px;
height: 32px;
border: none;
background: transparent;
color: #6b7280;
font-size: 18px;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.dropdown-close:hover {
background: #f3f4f6;
color: #1a1a1a;
}
.dropdown-body {
flex: 1;
overflow-y: auto;
padding: 16px;
max-height: 350px;
}
.empty-state {
text-align: center;
padding: 40px 20px;
color: #9ca3af;
font-size: 15px;
}
.dropdown-foot {
padding: 16px 20px;
border-top: 1px solid #e5e5e5;
display: flex;
flex-direction: column;
gap: 12px;
}
.cart-summary {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.summary-label {
font-size: 15px;
font-weight: 500;
color: #6b7280;
}
.summary-value {
font-size: 20px;
font-weight: 700;
color: #1a1a1a;
}
/* Buttons */
.btn-primary-full,
.btn-outline,
.btn-text {
display: block;
text-align: center;
padding: 12px 20px;
font-size: 15px;
font-weight: 500;
text-decoration: none;
border-radius: 8px;
transition: all 0.2s;
border: none;
cursor: pointer;
}
.btn-primary-full {
background: #6b46c1;
color: white;
}
.btn-primary-full:hover {
background: #5936a3;
}
.btn-outline {
background: transparent;
color: #6b46c1;
border: 1px solid #6b46c1;
}
.btn-outline:hover {
background: #f3f0ff;
}
.btn-text {
background: transparent;
color: #6b7280;
padding: 8px;
}
.btn-text:hover {
color: #1a1a1a;
}
/* Mobile Toggle */
.mobile-toggle {
display: none;
flex-direction: column;
gap: 5px;
width: 44px;
height: 44px;
padding: 10px;
border: none;
background: transparent;
cursor: pointer;
border-radius: 6px;
transition: background 0.2s;
}
.mobile-toggle:hover {
background: #f5f5f5;
}
.toggle-line {
width: 100%;
height: 2px;
background: #4a4a4a;
border-radius: 2px;
transition: all 0.3s;
}
/* Mobile Menu */
.mobile-menu {
position: fixed;
top: 0;
right: -100%;
width: 320px;
height: 100vh;
background: white;
box-shadow: -4px 0 16px rgba(0, 0, 0, 0.1);
z-index: 1002;
transition: right 0.3s ease;
display: flex;
flex-direction: column;
}
.mobile-menu.active {
right: 0;
}
.mobile-menu-header {
padding: 24px;
border-bottom: 1px solid #e5e5e5;
display: flex;
align-items: center;
justify-content: space-between;
}
.mobile-brand {
font-size: 18px;
font-weight: 600;
color: #1a1a1a;
}
.mobile-close {
width: 36px;
height: 36px;
border: none;
background: transparent;
color: #6b7280;
font-size: 20px;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.mobile-close:hover {
background: #f3f4f6;
}
.mobile-menu-list {
list-style: none;
margin: 0;
padding: 16px;
}
.mobile-menu-list li {
margin-bottom: 4px;
}
.mobile-link {
display: block;
padding: 14px 16px;
font-size: 16px;
font-weight: 500;
color: #4a4a4a;
text-decoration: none;
border-radius: 8px;
transition: all 0.2s;
}
.mobile-link:hover,
.mobile-link.active {
color: #6b46c1;
background: #f3f0ff;
}
/* Responsive Design */
@media (max-width: 1024px) {
.navbar-menu {
display: none;
}
.mobile-toggle {
display: flex;
}
}
@media (max-width: 640px) {
.navbar-wrapper {
padding: 0 16px;
height: 64px;
}
.brand-name {
font-size: 18px;
}
.brand-logo {
width: 40px;
height: 40px;
}
.action-dropdown {
width: 100vw;
max-width: 380px;
right: -16px;
}
}

View File

@@ -0,0 +1,299 @@
/* Cart and Wishlist Item Styles */
/* Cart Items */
.cart-item {
display: flex;
gap: 12px;
padding: 16px;
background: #fafafa;
border-radius: 8px;
margin-bottom: 12px;
transition: all 0.2s;
}
.cart-item:hover {
background: #f5f5f5;
}
.cart-item-image {
flex-shrink: 0;
width: 80px;
height: 80px;
border-radius: 6px;
overflow: hidden;
background: white;
}
.cart-item-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.cart-item-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
.cart-item-name {
margin: 0;
font-size: 14px;
font-weight: 600;
color: #1a1a1a;
line-height: 1.4;
}
.cart-item-price {
margin: 0;
font-size: 15px;
font-weight: 700;
color: #6b46c1;
}
.cart-item-quantity {
display: flex;
align-items: center;
gap: 8px;
margin-top: 4px;
}
.qty-btn {
width: 28px;
height: 28px;
border: 1px solid #d1d5db;
background: white;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
color: #6b7280;
}
.qty-btn:hover {
border-color: #6b46c1;
color: #6b46c1;
background: #f3f0ff;
}
.qty-value {
min-width: 32px;
text-align: center;
font-size: 14px;
font-weight: 600;
color: #1a1a1a;
}
.cart-item-actions {
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: space-between;
}
.cart-item-remove {
width: 32px;
height: 32px;
border: none;
background: transparent;
color: #9ca3af;
font-size: 16px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.cart-item-remove:hover {
background: #fee2e2;
color: #dc2626;
}
.cart-item-total {
margin: 0;
font-size: 16px;
font-weight: 700;
color: #1a1a1a;
}
/* Wishlist Items */
.wishlist-item {
display: flex;
gap: 12px;
padding: 16px;
background: #fafafa;
border-radius: 8px;
margin-bottom: 12px;
position: relative;
transition: all 0.2s;
}
.wishlist-item:hover {
background: #f5f5f5;
}
.wishlist-item-image {
flex-shrink: 0;
width: 80px;
height: 80px;
border-radius: 6px;
overflow: hidden;
background: white;
}
.wishlist-item-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.wishlist-item-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
.wishlist-item-name {
margin: 0;
font-size: 14px;
font-weight: 600;
color: #1a1a1a;
line-height: 1.4;
padding-right: 24px;
}
.wishlist-item-price {
margin: 0;
font-size: 15px;
font-weight: 700;
color: #6b46c1;
}
.btn-move-to-cart {
align-self: flex-start;
padding: 6px 14px;
border: 1px solid #6b46c1;
background: transparent;
color: #6b46c1;
font-size: 13px;
font-weight: 500;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
}
.btn-move-to-cart:hover {
background: #6b46c1;
color: white;
}
.wishlist-item-remove {
position: absolute;
top: 12px;
right: 12px;
width: 28px;
height: 28px;
border: none;
background: transparent;
color: #9ca3af;
font-size: 16px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.wishlist-item-remove:hover {
background: #fee2e2;
color: #dc2626;
}
/* Notifications */
.notification {
position: fixed;
bottom: 24px;
right: 24px;
min-width: 280px;
padding: 16px 20px;
background: white;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
display: flex;
align-items: center;
gap: 12px;
z-index: 10000;
transform: translateY(100px);
opacity: 0;
transition: all 0.3s ease;
font-family: 'Roboto', sans-serif;
}
.notification.show {
transform: translateY(0);
opacity: 1;
}
.notification i {
font-size: 20px;
flex-shrink: 0;
}
.notification-success {
border-left: 4px solid #10b981;
}
.notification-success i {
color: #10b981;
}
.notification-info {
border-left: 4px solid #3b82f6;
}
.notification-info i {
color: #3b82f6;
}
.notification span {
flex: 1;
font-size: 14px;
font-weight: 500;
color: #1a1a1a;
}
/* Mobile Responsive */
@media (max-width: 640px) {
.cart-item,
.wishlist-item {
padding: 12px;
}
.cart-item-image,
.wishlist-item-image {
width: 64px;
height: 64px;
}
.cart-item-name,
.wishlist-item-name {
font-size: 13px;
}
.notification {
right: 16px;
left: 16px;
min-width: auto;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

155
website/assets/js/admin.js Normal file
View File

@@ -0,0 +1,155 @@
// Sky Art Shop - Admin Panel Functions
// Delete confirmation with fetch
function deleteWithConfirmation(url, itemName, redirectUrl) {
if (!confirm(`Are you sure you want to delete ${itemName}?`)) {
return false;
}
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.ok) {
window.location.href = redirectUrl || window.location.href;
} else {
alert("Delete failed. Please try again.");
}
})
.catch((error) => {
alert("Error: " + error.message);
});
return false;
}
// Image upload with preview
function uploadImageWithPreview(fileInputId, previewId, urlInputId) {
const input = document.getElementById(fileInputId);
const file = input.files[0];
if (!file) {
alert("Please select a file");
return;
}
const formData = new FormData();
formData.append("file", file);
fetch("/admin/upload/image", {
method: "POST",
body: formData,
})
.then((response) => response.json())
.then((result) => {
if (result.success) {
document.getElementById(urlInputId).value = result.url;
const preview = document.getElementById(previewId);
if (preview) {
preview.src = result.url;
preview.style.display = "block";
}
showAdminNotification("Image uploaded successfully!", "success");
} else {
showAdminNotification("Upload failed: " + result.message, "error");
}
})
.catch((error) => {
showAdminNotification("Upload error: " + error.message, "error");
});
}
// Show admin notification
function showAdminNotification(message, type = "success") {
const notification = document.createElement("div");
notification.className = `alert alert-${
type === "success" ? "success" : "danger"
} alert-dismissible fade show`;
notification.style.cssText =
"position: fixed; top: 20px; right: 20px; z-index: 9999; min-width: 300px;";
notification.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.remove("show");
setTimeout(() => notification.remove(), 150);
}, 3000);
}
// Copy to clipboard
function copyToClipboard(text) {
navigator.clipboard
.writeText(text)
.then(() => {
showAdminNotification("Copied to clipboard!", "success");
})
.catch(() => {
showAdminNotification("Failed to copy", "error");
});
}
// Auto-generate slug from title
function generateSlugFromTitle(titleInputId, slugInputId) {
const titleInput = document.getElementById(titleInputId);
const slugInput = document.getElementById(slugInputId);
if (titleInput && slugInput) {
titleInput.addEventListener("input", function () {
if (slugInput.value === "" || slugInput.dataset.auto === "true") {
slugInput.value = titleInput.value
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)/g, "");
slugInput.dataset.auto = "true";
}
});
slugInput.addEventListener("input", function () {
slugInput.dataset.auto = "false";
});
}
}
// Form validation helper
function validateForm(formId) {
const form = document.getElementById(formId);
if (!form) return true;
const requiredFields = form.querySelectorAll("[required]");
let isValid = true;
requiredFields.forEach((field) => {
if (!field.value.trim()) {
field.classList.add("is-invalid");
isValid = false;
} else {
field.classList.remove("is-invalid");
}
});
if (!isValid) {
showAdminNotification("Please fill in all required fields", "error");
}
return isValid;
}
// Initialize tooltips
document.addEventListener("DOMContentLoaded", function () {
// Bootstrap tooltips
var tooltipTriggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="tooltip"]')
);
if (typeof bootstrap !== "undefined") {
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
}
});

378
website/assets/js/cart.js Normal file
View File

@@ -0,0 +1,378 @@
// Sky Art Shop - Shopping Cart Functions
// Add item to cart
function addToCart(id, name, price, imageUrl = null) {
// Get existing cart from localStorage
let cart = JSON.parse(localStorage.getItem("cart") || "[]");
// Check if item already exists
const existingItem = cart.find((item) => item.id === id);
if (existingItem) {
existingItem.quantity++;
// Update imageUrl if it was null before
if (!existingItem.imageUrl && imageUrl) {
existingItem.imageUrl = imageUrl;
}
} else {
cart.push({ id, name, price, quantity: 1, imageUrl });
}
// Save cart
localStorage.setItem("cart", JSON.stringify(cart));
console.log("Cart updated:", cart);
// Show confirmation
showCartNotification(`${name} added to cart!`);
updateCartCount();
}
// Remove item from cart
function removeFromCart(id) {
let cart = JSON.parse(localStorage.getItem("cart") || "[]");
cart = cart.filter((item) => item.id !== id);
localStorage.setItem("cart", JSON.stringify(cart));
updateCartCount();
}
// Update cart item quantity
function updateCartQuantity(id, quantity) {
let cart = JSON.parse(localStorage.getItem("cart") || "[]");
const item = cart.find((item) => item.id === id);
if (item) {
item.quantity = quantity;
if (quantity <= 0) {
cart = cart.filter((item) => item.id !== id);
}
}
localStorage.setItem("cart", JSON.stringify(cart));
updateCartCount();
}
// Get cart items
function getCart() {
return JSON.parse(localStorage.getItem("cart") || "[]");
}
// Get cart total
function getCartTotal() {
const cart = getCart();
return cart.reduce((total, item) => total + item.price * item.quantity, 0);
}
// Update cart count badge
function updateCartCount() {
const cart = getCart();
const count = cart.reduce((total, item) => total + item.quantity, 0);
// Update old badge (if exists)
const badge = document.getElementById("cart-count");
if (badge) {
badge.textContent = count;
badge.style.display = count > 0 ? "inline" : "none";
}
// Update navbar cart badge
const navCartBadge = document.querySelector("#cartBtn .badge");
if (navCartBadge) {
navCartBadge.textContent = count;
navCartBadge.style.display = count > 0 ? "block" : "none";
}
}
// Show cart notification
function showCartNotification(message) {
const notification = document.createElement("div");
notification.className = "cart-notification";
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 80px;
right: 20px;
background: #4CAF50;
color: white;
padding: 15px 25px;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
z-index: 10000;
animation: slideInFromTop 0.3s ease;
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = "slideOut 0.3s ease";
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// Clear entire cart
function clearCart() {
localStorage.removeItem("cart");
updateCartCount();
}
// ====================================
// Wishlist Functions
// ====================================
// Add item to wishlist
function addToWishlist(id, name, price, imageUrl) {
let wishlist = JSON.parse(localStorage.getItem("wishlist") || "[]");
const existingItem = wishlist.find((item) => item.id === id);
if (existingItem) {
showWishlistNotification(`${name} is already in your wishlist!`);
return;
}
wishlist.push({ id, name, price, imageUrl });
localStorage.setItem("wishlist", JSON.stringify(wishlist));
console.log("Wishlist updated:", wishlist);
showWishlistNotification(`${name} added to wishlist!`);
updateWishlistCount();
}
// Remove item from wishlist
function removeFromWishlist(id) {
let wishlist = JSON.parse(localStorage.getItem("wishlist") || "[]");
wishlist = wishlist.filter((item) => item.id !== id);
localStorage.setItem("wishlist", JSON.stringify(wishlist));
updateWishlistCount();
}
// Get wishlist items
function getWishlist() {
return JSON.parse(localStorage.getItem("wishlist") || "[]");
}
// Update wishlist count badge
function updateWishlistCount() {
const wishlist = getWishlist();
const count = wishlist.length;
const navWishlistBadge = document.querySelector("#wishlistBtn .badge");
const wishlistIcon = document.querySelector("#wishlistBtn i");
if (navWishlistBadge) {
navWishlistBadge.textContent = count;
navWishlistBadge.style.display = count > 0 ? "block" : "none";
}
// Change heart icon based on wishlist status
if (wishlistIcon) {
if (count > 0) {
wishlistIcon.className = "bi bi-heart-fill";
wishlistIcon.style.color = "#e74c3c";
} else {
wishlistIcon.className = "bi bi-heart";
wishlistIcon.style.color = "";
}
}
}
// Show wishlist notification
function showWishlistNotification(message) {
const notification = document.createElement("div");
notification.className = "wishlist-notification";
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 80px;
right: 20px;
background: #E91E63;
color: white;
padding: 15px 25px;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
z-index: 10000;
animation: slideInFromTop 0.3s ease;
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = "slideOut 0.3s ease";
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// Clear entire wishlist
function clearWishlist() {
localStorage.removeItem("wishlist");
updateWishlistCount();
}
// ====================================
// Dropdown Functions
// ====================================
// Render cart dropdown
function renderCartDropdown() {
const cart = getCart();
const cartItems = document.getElementById("cartItems");
const cartTotal = document.getElementById("cartTotal");
if (!cartItems) return;
if (cart.length === 0) {
cartItems.innerHTML = '<p class="empty-message">Your cart is empty</p>';
if (cartTotal) cartTotal.textContent = "$0.00";
return;
}
console.log("Rendering cart:", cart);
cartItems.innerHTML = cart
.map((item) => {
const imgSrc = item.imageUrl || "/assets/images/placeholder.jpg";
console.log("Cart item image URL:", imgSrc);
return `
<div class="dropdown-item">
<img src="${imgSrc}" alt="${
item.name
}" class="dropdown-item-image" onerror="this.src='/assets/images/placeholder.jpg'">
<div class="dropdown-item-info">
<div class="dropdown-item-name">${item.name}</div>
<div class="dropdown-item-details">
<span class="dropdown-item-quantity">Qty: ${
item.quantity
}</span>
<span class="dropdown-item-price">$${(
item.price * item.quantity
).toFixed(2)}</span>
</div>
</div>
<button class="dropdown-item-remove" onclick="removeFromCartDropdown('${
item.id
}')">
<i class="bi bi-x"></i>
</button>
</div>
`;
})
.join("");
if (cartTotal) {
const total = getCartTotal();
cartTotal.textContent = `$${total.toFixed(2)}`;
}
}
// Render wishlist dropdown
function renderWishlistDropdown() {
const wishlist = getWishlist();
const wishlistItems = document.getElementById("wishlistItems");
if (!wishlistItems) return;
if (wishlist.length === 0) {
wishlistItems.innerHTML =
'<p class="empty-message">Your wishlist is empty</p>';
return;
}
console.log("Rendering wishlist:", wishlist);
wishlistItems.innerHTML = wishlist
.map((item) => {
const imgSrc = item.imageUrl || "/assets/images/placeholder.jpg";
console.log("Wishlist item image URL:", imgSrc);
return `
<div class="dropdown-item">
<img src="${imgSrc}" alt="${
item.name
}" class="dropdown-item-image" onerror="this.src='/assets/images/placeholder.jpg'">
<div class="dropdown-item-info">
<div class="dropdown-item-name">${item.name}</div>
<div class="dropdown-item-details">
<span class="dropdown-item-price">$${item.price.toFixed(
2
)}</span>
</div>
</div>
<button class="dropdown-item-remove" onclick="removeFromWishlistDropdown('${
item.id
}')">
<i class="bi bi-x"></i>
</button>
</div>
`;
})
.join("");
}
// Remove from cart via dropdown
function removeFromCartDropdown(id) {
removeFromCart(id);
renderCartDropdown();
updateCartCount();
}
// Remove from wishlist via dropdown
function removeFromWishlistDropdown(id) {
removeFromWishlist(id);
renderWishlistDropdown();
updateWishlistCount();
}
// Toggle dropdown visibility
function toggleDropdown(dropdownId) {
const dropdown = document.getElementById(dropdownId);
if (!dropdown) return;
// Close other dropdowns
document.querySelectorAll(".icon-dropdown").forEach((d) => {
if (d.id !== dropdownId) {
d.classList.remove("show");
}
});
dropdown.classList.toggle("show");
// Render content when opening
if (dropdown.classList.contains("show")) {
if (dropdownId === "cartDropdown") {
renderCartDropdown();
} else if (dropdownId === "wishlistDropdown") {
renderWishlistDropdown();
}
}
}
// Close cart/wishlist dropdowns when clicking outside
document.addEventListener("click", function (e) {
if (
!e.target.closest(".dropdown-container") &&
!e.target.closest(".nav-toggle")
) {
document.querySelectorAll(".icon-dropdown").forEach((d) => {
d.classList.remove("show");
});
}
});
// Initialize cart and wishlist count on page load
document.addEventListener("DOMContentLoaded", function () {
updateCartCount();
updateWishlistCount();
// Add click handlers for dropdown toggles
const cartBtn = document.getElementById("cartBtn");
const wishlistBtn = document.getElementById("wishlistBtn");
if (cartBtn) {
cartBtn.addEventListener("click", function (e) {
e.preventDefault();
toggleDropdown("cartDropdown");
});
}
if (wishlistBtn) {
wishlistBtn.addEventListener("click", function (e) {
e.preventDefault();
toggleDropdown("wishlistDropdown");
});
}
});

427
website/assets/js/main.js Normal file
View File

@@ -0,0 +1,427 @@
// Sky Art Shop - Main JavaScript File
// ====================================
// Mobile Navigation Toggle
// ====================================
document.addEventListener("DOMContentLoaded", function () {
const navToggle = document.querySelector(".nav-toggle");
const navMenu = document.querySelector("#navDropdown");
if (navToggle && navMenu) {
// Hover to open dropdown
navToggle.addEventListener("mouseenter", function () {
navMenu.classList.add("active");
this.setAttribute("aria-expanded", "true");
const spans = this.querySelectorAll("span");
spans[0].style.transform = "rotate(45deg) translate(7px, 7px)";
spans[1].style.opacity = "0";
spans[2].style.transform = "rotate(-45deg) translate(7px, -7px)";
});
// Keep dropdown open when hovering over it
navMenu.addEventListener("mouseenter", function () {
this.classList.add("active");
});
// Close when mouse leaves both hamburger and dropdown
navToggle.addEventListener("mouseleave", function (e) {
// Delay closing to allow moving to dropdown
setTimeout(() => {
if (!navMenu.matches(":hover") && !navToggle.matches(":hover")) {
navMenu.classList.remove("active");
navToggle.setAttribute("aria-expanded", "false");
const spans = navToggle.querySelectorAll("span");
spans[0].style.transform = "none";
spans[1].style.opacity = "1";
spans[2].style.transform = "none";
}
}, 200);
});
navMenu.addEventListener("mouseleave", function () {
setTimeout(() => {
if (!navMenu.matches(":hover") && !navToggle.matches(":hover")) {
navMenu.classList.remove("active");
navToggle.setAttribute("aria-expanded", "false");
const spans = navToggle.querySelectorAll("span");
spans[0].style.transform = "none";
spans[1].style.opacity = "1";
spans[2].style.transform = "none";
}
}, 200);
});
// Click to toggle (for mobile/touch)
navToggle.addEventListener("click", function (e) {
e.stopPropagation();
const isActive = navMenu.classList.toggle("active");
this.setAttribute("aria-expanded", isActive ? "true" : "false");
// Animate hamburger menu
const spans = this.querySelectorAll("span");
if (isActive) {
spans[0].style.transform = "rotate(45deg) translate(7px, 7px)";
spans[1].style.opacity = "0";
spans[2].style.transform = "rotate(-45deg) translate(7px, -7px)";
} else {
spans[0].style.transform = "none";
spans[1].style.opacity = "1";
spans[2].style.transform = "none";
}
});
// Close dropdown when clicking on a link
const dropdownLinks = navMenu.querySelectorAll("a");
dropdownLinks.forEach((link) => {
link.addEventListener("click", function () {
navMenu.classList.remove("active");
navToggle.setAttribute("aria-expanded", "false");
const spans = navToggle.querySelectorAll("span");
spans[0].style.transform = "none";
spans[1].style.opacity = "1";
spans[2].style.transform = "none";
});
});
// Close dropdown when clicking outside
document.addEventListener("click", function (event) {
// Don't close if clicking on cart/wishlist dropdowns
if (
event.target.closest(".dropdown-container") ||
event.target.closest(".icon-dropdown")
) {
return;
}
const isClickInside =
navToggle.contains(event.target) || navMenu.contains(event.target);
if (!isClickInside && navMenu.classList.contains("active")) {
navMenu.classList.remove("active");
navToggle.setAttribute("aria-expanded", "false");
const spans = navToggle.querySelectorAll("span");
spans[0].style.transform = "none";
spans[1].style.opacity = "1";
spans[2].style.transform = "none";
}
});
}
});
// ====================================
// Smooth Scrolling for Anchor Links
// ====================================
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", function (e) {
const href = this.getAttribute("href");
if (href !== "#" && href !== "#instagram" && href !== "#wishlist") {
e.preventDefault();
const target = document.querySelector(href);
if (target) {
target.scrollIntoView({
behavior: "smooth",
block: "start",
});
}
}
});
});
// ====================================
// Shop Page Filtering
// ====================================
const categoryFilter = document.getElementById("category-filter");
const sortFilter = document.getElementById("sort-filter");
if (categoryFilter) {
categoryFilter.addEventListener("change", function () {
const selectedCategory = this.value;
const productCards = document.querySelectorAll(".product-card");
productCards.forEach((card) => {
if (selectedCategory === "all") {
card.style.display = "block";
} else {
const cardCategory = card.getAttribute("data-category");
if (cardCategory === selectedCategory) {
card.style.display = "block";
} else {
card.style.display = "none";
}
}
});
});
}
if (sortFilter) {
sortFilter.addEventListener("change", function () {
const sortValue = this.value;
const productsGrid = document.querySelector(".products-grid");
const productCards = Array.from(document.querySelectorAll(".product-card"));
if (sortValue === "price-low") {
productCards.sort((a, b) => {
const priceA = parseFloat(
a.querySelector(".price").textContent.replace("$", "")
);
const priceB = parseFloat(
b.querySelector(".price").textContent.replace("$", "")
);
return priceA - priceB;
});
} else if (sortValue === "price-high") {
productCards.sort((a, b) => {
const priceA = parseFloat(
a.querySelector(".price").textContent.replace("$", "")
);
const priceB = parseFloat(
b.querySelector(".price").textContent.replace("$", "")
);
return priceB - priceA;
});
}
// Re-append sorted cards
productCards.forEach((card) => {
productsGrid.appendChild(card);
});
});
}
// ====================================
// Add to Cart Functionality (Basic)
// ====================================
document.querySelectorAll(".product-card .btn").forEach((button) => {
button.addEventListener("click", function (e) {
e.preventDefault();
// Get product details
const productCard = this.closest(".product-card");
const productName = productCard.querySelector("h3").textContent;
const productPrice = productCard.querySelector(".price").textContent;
// Show notification
showNotification(`${productName} added to cart!`);
// You can expand this to actually store cart items
// For example, using localStorage or sending to a server
});
});
// ====================================
// Contact Form Handling
// ====================================
const contactForm = document.getElementById("contactForm");
if (contactForm) {
contactForm.addEventListener("submit", function (e) {
e.preventDefault();
// Get form values
const name = document.getElementById("name").value;
const email = document.getElementById("email").value;
const phone = document.getElementById("phone").value;
const subject = document.getElementById("subject").value;
const message = document.getElementById("message").value;
// Basic validation
if (!name || !email || !subject || !message) {
showNotification("Please fill in all required fields!", "error");
return;
}
// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showNotification("Please enter a valid email address!", "error");
return;
}
// Here you would typically send the form data to a server
// For now, we'll just show a success message
showNotification(
"Thank you! Your message has been sent. We'll get back to you soon.",
"success"
);
// Reset form
contactForm.reset();
});
}
// ====================================
// Notification System
// ====================================
function showNotification(message, type = "success") {
// Create notification element
const notification = document.createElement("div");
notification.className = `notification notification-${type}`;
notification.textContent = message;
// Style the notification
notification.style.cssText = `
position: fixed;
top: 100px;
right: 20px;
background-color: ${type === "success" ? "#4CAF50" : "#F44336"};
color: white;
padding: 15px 25px;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
z-index: 10000;
animation: slideIn 0.3s ease-out;
`;
// Add animation
const style = document.createElement("style");
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(400px);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// Add to page
document.body.appendChild(notification);
// Remove after 3 seconds
setTimeout(() => {
notification.style.animation = "slideOut 0.3s ease-out";
setTimeout(() => {
notification.remove();
}, 300);
}, 3000);
}
// ====================================
// Image Lazy Loading (Optional Enhancement)
// ====================================
if ("IntersectionObserver" in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src || img.src;
img.classList.add("loaded");
observer.unobserve(img);
}
});
});
document.querySelectorAll("img").forEach((img) => {
imageObserver.observe(img);
});
}
// ====================================
// Scroll to Top Button
// ====================================
function createScrollToTopButton() {
const button = document.createElement("button");
button.innerHTML = "↑";
button.className = "scroll-to-top";
button.style.cssText = `
position: fixed;
bottom: 30px;
right: 30px;
width: 50px;
height: 50px;
background-color: #6B4E9B;
color: white;
border: none;
border-radius: 50%;
font-size: 24px;
cursor: pointer;
display: none;
z-index: 1000;
transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
`;
document.body.appendChild(button);
// Show/hide button based on scroll position
window.addEventListener("scroll", () => {
if (window.pageYOffset > 300) {
button.style.display = "block";
} else {
button.style.display = "none";
}
});
// Scroll to top when clicked
button.addEventListener("click", () => {
window.scrollTo({
top: 0,
behavior: "smooth",
});
});
// Hover effect
button.addEventListener("mouseenter", () => {
button.style.backgroundColor = "#5a3e82";
button.style.transform = "translateY(-3px)";
});
button.addEventListener("mouseleave", () => {
button.style.backgroundColor = "#6B4E9B";
button.style.transform = "translateY(0)";
});
}
// Initialize scroll to top button
createScrollToTopButton();
// ====================================
// Portfolio Gallery Hover Effects
// ====================================
document.querySelectorAll(".portfolio-category").forEach((category) => {
category.addEventListener("mouseenter", function () {
this.style.transition = "all 0.3s ease";
});
});
// ====================================
// Active Navigation Link Highlighting
// ====================================
function highlightActiveNavLink() {
const currentPage = window.location.pathname.split("/").pop() || "index.html";
const navLinks = document.querySelectorAll(".nav-menu a");
navLinks.forEach((link) => {
const linkPage = link.getAttribute("href").split("/").pop().split("#")[0];
if (linkPage === currentPage) {
link.classList.add("active");
}
});
}
highlightActiveNavLink();
// ====================================
// Print console message
// ====================================
console.log(
"%c Sky Art Shop Website ",
"background: #6B4E9B; color: white; font-size: 20px; padding: 10px;"
);
console.log("Welcome to Sky Art Shop! 🎨");

View File

@@ -0,0 +1,376 @@
/**
* Enhanced Cart and Wishlist Management System
* Amazon/eBay-style product display with images and details
*/
class ShoppingManager {
constructor() {
this.cart = this.loadFromStorage("skyart_cart") || [];
this.wishlist = this.loadFromStorage("skyart_wishlist") || [];
this.init();
}
init() {
this.updateAllBadges();
this.setupEventListeners();
this.renderCart();
this.renderWishlist();
}
loadFromStorage(key) {
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
} catch (e) {
console.error("Error loading from storage:", e);
return null;
}
}
saveToStorage(key, data) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (e) {
console.error("Error saving to storage:", e);
}
}
setupEventListeners() {
// Cart toggle
const cartToggle = document.getElementById("cartToggle");
const cartPanel = document.getElementById("cartPanel");
const cartClose = document.getElementById("cartClose");
if (cartToggle) {
cartToggle.addEventListener("click", (e) => {
e.stopPropagation();
cartPanel?.classList.toggle("active");
document.getElementById("wishlistPanel")?.classList.remove("active");
});
}
if (cartClose) {
cartClose.addEventListener("click", (e) => {
e.stopPropagation();
cartPanel?.classList.remove("active");
});
}
// Wishlist toggle
const wishlistToggle = document.getElementById("wishlistToggle");
const wishlistPanel = document.getElementById("wishlistPanel");
const wishlistClose = document.getElementById("wishlistClose");
if (wishlistToggle) {
wishlistToggle.addEventListener("click", (e) => {
e.stopPropagation();
wishlistPanel?.classList.toggle("active");
cartPanel?.classList.remove("active");
});
}
if (wishlistClose) {
wishlistClose.addEventListener("click", (e) => {
e.stopPropagation();
wishlistPanel?.classList.remove("active");
});
}
// Mobile menu
const mobileToggle = document.getElementById("mobileMenuToggle");
const mobileMenu = document.getElementById("mobileMenu");
const mobileClose = document.getElementById("mobileMenuClose");
if (mobileToggle) {
mobileToggle.addEventListener("click", () => {
mobileMenu?.classList.toggle("active");
document.body.style.overflow = mobileMenu?.classList.contains("active")
? "hidden"
: "";
});
}
if (mobileClose) {
mobileClose.addEventListener("click", () => {
mobileMenu?.classList.remove("active");
document.body.style.overflow = "";
});
}
// Close dropdowns on outside click
document.addEventListener("click", (e) => {
if (!e.target.closest(".cart-dropdown-wrapper")) {
cartPanel?.classList.remove("active");
}
if (!e.target.closest(".wishlist-dropdown-wrapper")) {
wishlistPanel?.classList.remove("active");
}
});
}
// Add to Cart
addToCart(product, quantity = 1) {
const existingItem = this.cart.find((item) => item.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.cart.push({
id: product.id,
name: product.name,
price: parseFloat(product.price),
imageurl: product.imageurl,
quantity: quantity,
addedAt: new Date().toISOString(),
});
}
this.saveToStorage("skyart_cart", this.cart);
this.updateAllBadges();
this.renderCart();
this.showNotification(`${product.name} added to cart!`, "success");
}
// Remove from Cart
removeFromCart(productId) {
this.cart = this.cart.filter((item) => item.id !== productId);
this.saveToStorage("skyart_cart", this.cart);
this.updateAllBadges();
this.renderCart();
this.showNotification("Item removed from cart", "info");
}
// Update Cart Quantity
updateCartQuantity(productId, quantity) {
const item = this.cart.find((item) => item.id === productId);
if (item) {
if (quantity <= 0) {
this.removeFromCart(productId);
} else {
item.quantity = quantity;
this.saveToStorage("skyart_cart", this.cart);
this.updateAllBadges();
this.renderCart();
}
}
}
// Add to Wishlist
addToWishlist(product) {
const exists = this.wishlist.find((item) => item.id === product.id);
if (!exists) {
this.wishlist.push({
id: product.id,
name: product.name,
price: parseFloat(product.price),
imageurl: product.imageurl,
addedAt: new Date().toISOString(),
});
this.saveToStorage("skyart_wishlist", this.wishlist);
this.updateAllBadges();
this.renderWishlist();
this.showNotification(`${product.name} added to wishlist!`, "success");
} else {
this.showNotification("Already in wishlist", "info");
}
}
// Remove from Wishlist
removeFromWishlist(productId) {
this.wishlist = this.wishlist.filter((item) => item.id !== productId);
this.saveToStorage("skyart_wishlist", this.wishlist);
this.updateAllBadges();
this.renderWishlist();
this.showNotification("Item removed from wishlist", "info");
}
// Move from Wishlist to Cart
moveToCart(productId) {
const item = this.wishlist.find((item) => item.id === productId);
if (item) {
this.addToCart(item, 1);
this.removeFromWishlist(productId);
}
}
// Update All Badges
updateAllBadges() {
const cartCount = this.cart.reduce((sum, item) => sum + item.quantity, 0);
const wishlistCount = this.wishlist.length;
const cartBadge = document.getElementById("cartCount");
const wishlistBadge = document.getElementById("wishlistCount");
if (cartBadge) {
cartBadge.textContent = cartCount;
cartBadge.style.display = cartCount > 0 ? "flex" : "none";
}
if (wishlistBadge) {
wishlistBadge.textContent = wishlistCount;
wishlistBadge.style.display = wishlistCount > 0 ? "flex" : "none";
}
}
// Render Cart
renderCart() {
const cartContent = document.getElementById("cartContent");
const cartSubtotal = document.getElementById("cartSubtotal");
if (!cartContent) return;
if (this.cart.length === 0) {
cartContent.innerHTML = '<p class="empty-state">Your cart is empty</p>';
if (cartSubtotal) cartSubtotal.textContent = "$0.00";
return;
}
const subtotal = this.cart.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
cartContent.innerHTML = this.cart
.map(
(item) => `
<div class="cart-item" data-product-id="${item.id}">
<div class="cart-item-image">
<img src="${item.imageurl || "/assets/images/placeholder.jpg"}"
alt="${item.name}"
onerror="this.src='/assets/images/placeholder.jpg'" />
</div>
<div class="cart-item-details">
<h4 class="cart-item-name">${item.name}</h4>
<p class="cart-item-price">$${item.price.toFixed(2)}</p>
<div class="cart-item-quantity">
<button class="qty-btn" onclick="shoppingManager.updateCartQuantity('${
item.id
}', ${item.quantity - 1})">
<i class="bi bi-dash"></i>
</button>
<span class="qty-value">${item.quantity}</span>
<button class="qty-btn" onclick="shoppingManager.updateCartQuantity('${
item.id
}', ${item.quantity + 1})">
<i class="bi bi-plus"></i>
</button>
</div>
</div>
<div class="cart-item-actions">
<button class="cart-item-remove" onclick="shoppingManager.removeFromCart('${
item.id
}')" title="Remove">
<i class="bi bi-trash"></i>
</button>
<p class="cart-item-total">$${(item.price * item.quantity).toFixed(
2
)}</p>
</div>
</div>
`
)
.join("");
if (cartSubtotal) {
cartSubtotal.textContent = `$${subtotal.toFixed(2)}`;
}
}
// Render Wishlist
renderWishlist() {
const wishlistContent = document.getElementById("wishlistContent");
if (!wishlistContent) return;
if (this.wishlist.length === 0) {
wishlistContent.innerHTML =
'<p class="empty-state">Your wishlist is empty</p>';
return;
}
wishlistContent.innerHTML = this.wishlist
.map(
(item) => `
<div class="wishlist-item" data-product-id="${item.id}">
<div class="wishlist-item-image">
<img src="${item.imageurl || "/assets/images/placeholder.jpg"}"
alt="${item.name}"
onerror="this.src='/assets/images/placeholder.jpg'" />
</div>
<div class="wishlist-item-details">
<h4 class="wishlist-item-name">${item.name}</h4>
<p class="wishlist-item-price">$${item.price.toFixed(2)}</p>
<button class="btn-move-to-cart" onclick="shoppingManager.moveToCart('${
item.id
}')">
<i class="bi bi-cart-plus"></i> Add to Cart
</button>
</div>
<button class="wishlist-item-remove" onclick="shoppingManager.removeFromWishlist('${
item.id
}')" title="Remove">
<i class="bi bi-x-lg"></i>
</button>
</div>
`
)
.join("");
}
// Show Notification
showNotification(message, type = "info") {
const notification = document.createElement("div");
notification.className = `notification notification-${type}`;
notification.innerHTML = `
<i class="bi bi-${
type === "success" ? "check-circle" : "info-circle"
}"></i>
<span>${message}</span>
`;
document.body.appendChild(notification);
setTimeout(() => notification.classList.add("show"), 10);
setTimeout(() => {
notification.classList.remove("show");
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// Get Cart Total
getCartTotal() {
return this.cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
// Get Cart Count
getCartCount() {
return this.cart.reduce((sum, item) => sum + item.quantity, 0);
}
// Clear Cart
clearCart() {
this.cart = [];
this.saveToStorage("skyart_cart", this.cart);
this.updateAllBadges();
this.renderCart();
}
}
// Initialize Shopping Manager
const shoppingManager = new ShoppingManager();
// Make it globally available
window.shoppingManager = shoppingManager;
// Navigation active state
document.addEventListener("DOMContentLoaded", () => {
const currentPage = window.location.pathname.split("/").pop() || "home.html";
document.querySelectorAll(".nav-link, .mobile-link").forEach((link) => {
const linkPage = link.getAttribute("href")?.split("/").pop();
if (linkPage === currentPage) {
link.classList.add("active");
}
});
});

255
website/public/about.html Normal file
View File

@@ -0,0 +1,255 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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/main.css" />
<link rel="stylesheet" href="/assets/css/navbar.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
</head>
<body>
<!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link active">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<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">
<h2>Our Story</h2>
<p>Sky Art Shop specializes in scrapbooking, journaling, cardmaking, and collaging stationery. We are passionate about helping people express their creativity and preserve their memories.</p>
<p>Our mission is to promote mental health and wellness through creative art activities. We believe that crafting is more than just a hobby—it's a therapeutic journey that brings joy, mindfulness, and self-expression.</p>
<h2>What We Offer</h2>
<p>Our carefully curated collection includes:</p>
<ul>
<li>Washi tape in various designs and patterns</li>
<li>Unique stickers for journaling and scrapbooking</li>
<li>High-quality journals and notebooks</li>
<li>Card making supplies and kits</li>
<li>Collage materials and ephemera</li>
<li>Creative tools and accessories</li>
</ul>
<h2>Why Choose Us</h2>
<p>We hand-select every item in our store to ensure the highest quality and uniqueness. Whether you're a seasoned crafter or just starting your creative journey, we have something special for everyone.</p>
<p>Join our community of creative minds and let your imagination soar!</p>
</div>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<h2>Sky Art Shop</h2>
<p>Follow Us</p>
<div class="social-links">
<a href="#instagram" aria-label="Instagram"><i class="bi bi-instagram"></i></a>
</div>
</div>
<div class="footer-links">
<h3>Quick Links</h3>
<ul>
<li><a href="/shop.html">Shop</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/contact.html">Contact</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© 2025 by Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/cart.js"></script>
<script src="/assets/js/shopping.js"></script>
</body>
</html>

255
website/public/blog.html Normal file
View File

@@ -0,0 +1,255 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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/main.css" />
<link rel="stylesheet" href="/assets/css/navbar.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
</head>
<body>
<!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link active">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<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">
<h2>Our Story</h2>
<p>Sky Art Shop specializes in scrapbooking, journaling, cardmaking, and collaging stationery. We are passionate about helping people express their creativity and preserve their memories.</p>
<p>Our mission is to promote mental health and wellness through creative art activities. We believe that crafting is more than just a hobby—it's a therapeutic journey that brings joy, mindfulness, and self-expression.</p>
<h2>What We Offer</h2>
<p>Our carefully curated collection includes:</p>
<ul>
<li>Washi tape in various designs and patterns</li>
<li>Unique stickers for journaling and scrapbooking</li>
<li>High-quality journals and notebooks</li>
<li>Card making supplies and kits</li>
<li>Collage materials and ephemera</li>
<li>Creative tools and accessories</li>
</ul>
<h2>Why Choose Us</h2>
<p>We hand-select every item in our store to ensure the highest quality and uniqueness. Whether you're a seasoned crafter or just starting your creative journey, we have something special for everyone.</p>
<p>Join our community of creative minds and let your imagination soar!</p>
</div>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<h2>Sky Art Shop</h2>
<p>Follow Us</p>
<div class="social-links">
<a href="#instagram" aria-label="Instagram"><i class="bi bi-instagram"></i></a>
</div>
</div>
<div class="footer-links">
<h3>Quick Links</h3>
<ul>
<li><a href="/shop.html">Shop</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/contact.html">Contact</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© 2025 by Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/cart.js"></script>
<script src="/assets/js/shopping.js"></script>
</body>
</html>

255
website/public/contact.html Normal file
View File

@@ -0,0 +1,255 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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/main.css" />
<link rel="stylesheet" href="/assets/css/navbar.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
</head>
<body>
<!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<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">
<h2>Our Story</h2>
<p>Sky Art Shop specializes in scrapbooking, journaling, cardmaking, and collaging stationery. We are passionate about helping people express their creativity and preserve their memories.</p>
<p>Our mission is to promote mental health and wellness through creative art activities. We believe that crafting is more than just a hobby—it's a therapeutic journey that brings joy, mindfulness, and self-expression.</p>
<h2>What We Offer</h2>
<p>Our carefully curated collection includes:</p>
<ul>
<li>Washi tape in various designs and patterns</li>
<li>Unique stickers for journaling and scrapbooking</li>
<li>High-quality journals and notebooks</li>
<li>Card making supplies and kits</li>
<li>Collage materials and ephemera</li>
<li>Creative tools and accessories</li>
</ul>
<h2>Why Choose Us</h2>
<p>We hand-select every item in our store to ensure the highest quality and uniqueness. Whether you're a seasoned crafter or just starting your creative journey, we have something special for everyone.</p>
<p>Join our community of creative minds and let your imagination soar!</p>
</div>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<h2>Sky Art Shop</h2>
<p>Follow Us</p>
<div class="social-links">
<a href="#instagram" aria-label="Instagram"><i class="bi bi-instagram"></i></a>
</div>
</div>
<div class="footer-links">
<h3>Quick Links</h3>
<ul>
<li><a href="/shop.html">Shop</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/contact.html">Contact</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© 2025 by Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/cart.js"></script>
<script src="/assets/js/shopping.js"></script>
</body>
</html>

View File

@@ -0,0 +1,208 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Sky Art Shop - Scrapbooking, journaling, cardmaking, and collaging stationery." />
<title>Home - Sky Art Shop</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
<link rel="stylesheet" href="/assets/css/main.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
<style>
/* Include navbar styles inline for now */
<?php include '/var/www/skyartshop/components/navbar.html'; ?>
</style>
</head>
<body>
<!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link active">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<!-- Hero Section -->
<section class="hero" style="padding: 80px 24px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; text-align: center;">
<div class="hero-content" style="max-width: 800px; margin: 0 auto;">
<h2 style="font-size: 48px; font-weight: 700; margin-bottom: 16px; line-height: 1.2;">Welcome to Sky Art Shop</h2>
<p style="font-size: 20px; margin-bottom: 24px; opacity: 0.95;">Your destination for creative stationery and supplies</p>
<div class="hero-description" style="margin-bottom: 32px;">
<p style="font-size: 16px; opacity: 0.9; line-height: 1.6;">
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.html" class="btn" style="display: inline-block; padding: 14px 32px; background: white; color: #667eea; font-weight: 600; border-radius: 8px; text-decoration: none; transition: transform 0.2s;">Shop Now</a>
</div>
</section>
<!-- Featured Products Section -->
<section class="collection" style="padding: 80px 24px; background: #f9fafb;">
<div class="container" style="max-width: 1200px; margin: 0 auto;">
<h2 style="text-align: center; font-size: 36px; font-weight: 700; color: #1a1a1a; margin-bottom: 12px;">Featured Products</h2>
<p style="text-align: center; color: #6b7280; margin-bottom: 48px; font-size: 16px;">Discover our most popular items</p>
<div class="products-grid" id="featuredProducts" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 24px; margin-bottom: 48px;">
<div style="text-align: center; padding: 40px; color: #9ca3af;">Loading products...</div>
</div>
<div style="text-align: center;">
<a href="/shop.html" class="btn" style="display: inline-block; padding: 12px 28px; background: #6b46c1; color: white; font-weight: 600; border-radius: 8px; text-decoration: none;">View All Products</a>
</div>
</div>
</section>
<!-- Footer -->
<footer style="padding: 40px 24px; background: #1f2937; color: white; text-align: center;">
<div style="max-width: 1200px; margin: 0 auto;">
<h3 style="font-size: 24px; font-weight: 600; margin-bottom: 16px;">Sky Art Shop</h3>
<p style="margin-bottom: 20px; color: #9ca3af;">Follow Us</p>
<div style="display: flex; gap: 16px; justify-content: center; margin-bottom: 24px;">
<a href="#" style="color: white; font-size: 24px;"><i class="bi bi-instagram"></i></a>
<a href="#" style="color: white; font-size: 24px;"><i class="bi bi-facebook"></i></a>
<a href="#" style="color: white; font-size: 24px;"><i class="bi bi-twitter"></i></a>
</div>
<p style="color: #9ca3af; font-size: 14px;">&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</footer>
<!-- Load Navbar Styles -->
<script>
// Inject navbar styles
const navStyles = document.createElement('style');
navStyles.textContent = `
${document.querySelector('style').textContent}
`;
</script>
<script src="/assets/js/shopping.js"></script>
<script>
// Load featured products
async function loadFeaturedProducts() {
try {
const response = await fetch('/api/products/featured?limit=4');
const data = await response.json();
const container = document.getElementById('featuredProducts');
if (data.success && data.products && data.products.length > 0) {
container.innerHTML = data.products.map(product => `
<div class="product-card" style="background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); transition: transform 0.2s, box-shadow 0.2s;">
<div class="product-image" style="position: relative; padding-top: 100%; overflow: hidden; background: #f3f4f6;">
<img src="${product.imageurl || '/assets/images/placeholder.jpg'}"
alt="${product.name}"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover;"
onerror="this.src='/assets/images/placeholder.jpg'" />
</div>
<div style="padding: 20px;">
<h3 style="font-size: 16px; font-weight: 600; color: #1a1a1a; margin: 0 0 8px 0; line-height: 1.4;">${product.name}</h3>
<p style="font-size: 20px; font-weight: 700; color: #6b46c1; margin: 0 0 16px 0;">$${parseFloat(product.price).toFixed(2)}</p>
<div style="display: flex; gap: 8px;">
<button onclick='shoppingManager.addToCart(${JSON.stringify(product)})'
style="flex: 1; padding: 10px; background: #6b46c1; color: white; border: none; border-radius: 6px; font-weight: 500; cursor: pointer; transition: background 0.2s;">
<i class="bi bi-cart-plus"></i> Add to Cart
</button>
<button onclick='shoppingManager.addToWishlist(${JSON.stringify(product)})'
style="width: 44px; padding: 10px; background: transparent; color: #6b46c1; border: 1px solid #6b46c1; border-radius: 6px; cursor: pointer; transition: all 0.2s;">
<i class="bi bi-heart"></i>
</button>
</div>
</div>
</div>
`).join('');
} else {
container.innerHTML = '<p style="text-align: center; color: #9ca3af; padding: 40px;">No products available</p>';
}
} catch (error) {
console.error('Error loading products:', error);
document.getElementById('featuredProducts').innerHTML = '<p style="text-align: center; color: #ef4444; padding: 40px;">Error loading products</p>';
}
}
// Load products on page load
loadFeaturedProducts();
</script>
</body>
</html>

313
website/public/home.html Normal file
View File

@@ -0,0 +1,313 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Sky Art Shop - Scrapbooking, journaling, cardmaking, and collaging stationery."
/>
<title>Home - Sky Art Shop</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
/>
<link rel="stylesheet" href="/assets/css/main.css" />
<link rel="stylesheet" href="/assets/css/navbar.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
</head>
<body> <!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<!-- Hero Section -->
<section class="hero">
<div class="hero-content">
<h2>Welcome to Sky Art Shop</h2>
<p>Your destination for creative stationery and supplies</p>
<div class="hero-description">
<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.html" class="btn btn-primary">Shop Now</a>
</div>
<div class="hero-image">
<img
src="/assets/images/hero-image.jpg"
alt="Sky Art Shop"
loading="lazy"
onerror="this.style.display='none'"
/>
</div>
</section>
<!-- Inspiration Section -->
<section class="inspiration">
<div class="container">
<h2>Get Inspired</h2>
<div class="inspiration-content">
<div class="inspiration-text">
<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">
<img
src="/assets/images/inspiration.jpg"
alt="Creative Inspiration"
loading="lazy"
onerror="this.style.display='none'"
/>
</div>
</div>
<a href="/portfolio.html" class="btn btn-secondary">View Portfolio</a>
</div>
</section>
<!-- Featured Products Section -->
<section class="collection">
<div class="container">
<h2>Featured Products</h2>
<p class="section-subtitle">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.jpg"
alt="Product"
loading="lazy"
/>
</div>
<h3>Loading products...</h3>
</div>
</div>
<a href="/shop.html" class="btn btn-secondary">View All Products</a>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<h2 id="footerSiteName">Sky Art Shop</h2>
<p>Follow Us</p>
<div class="social-links">
<a href="#instagram" aria-label="Instagram">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor"
>
<path
d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"
/>
</svg>
</a>
</div>
</div>
<div class="footer-links">
<h3>Quick Links</h3>
<ul>
<li><a href="/shop.html">Shop</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/contact.html">Contact</a></li>
<li><a href="/admin/login.html">Admin</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p id="footerText">© 2025 by Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/cart.js"></script>
<script>
// 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 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) => `
<div class="product-card">
<a href="/product.html?id=${
product.id
}" class="product-link">
<div class="product-image">
<img src="${
product.imageurl ||
"/assets/images/placeholder.jpg"
}" alt="${
product.name
}" loading="lazy" />
</div>
<h3>${product.name}</h3>
<p class="price">$${parseFloat(
product.price
).toFixed(2)}</p>
</a>
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
<button class="btn btn-small btn-icon" onclick="addToWishlist('${
product.id
}', '${product.name}', ${product.price}, '${
product.imageurl || ""
}')" 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}, '${
product.imageurl || ""
}')" 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");
}
}
// Initialize
loadSiteSettings();
loadFeaturedProducts();
</script>
<script src="/assets/js/shopping.js"></script>
</body>
</html>

15
website/public/index.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sky Art Shop</title>
<script>
// Redirect to home page
window.location.href = '/home.html';
</script>
</head>
<body>
<p>Loading Sky Art Shop...</p>
</body>
</html>

View File

@@ -0,0 +1,255 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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/main.css" />
<link rel="stylesheet" href="/assets/css/navbar.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
</head>
<body>
<!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link active">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<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">
<h2>Our Story</h2>
<p>Sky Art Shop specializes in scrapbooking, journaling, cardmaking, and collaging stationery. We are passionate about helping people express their creativity and preserve their memories.</p>
<p>Our mission is to promote mental health and wellness through creative art activities. We believe that crafting is more than just a hobby—it's a therapeutic journey that brings joy, mindfulness, and self-expression.</p>
<h2>What We Offer</h2>
<p>Our carefully curated collection includes:</p>
<ul>
<li>Washi tape in various designs and patterns</li>
<li>Unique stickers for journaling and scrapbooking</li>
<li>High-quality journals and notebooks</li>
<li>Card making supplies and kits</li>
<li>Collage materials and ephemera</li>
<li>Creative tools and accessories</li>
</ul>
<h2>Why Choose Us</h2>
<p>We hand-select every item in our store to ensure the highest quality and uniqueness. Whether you're a seasoned crafter or just starting your creative journey, we have something special for everyone.</p>
<p>Join our community of creative minds and let your imagination soar!</p>
</div>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<h2>Sky Art Shop</h2>
<p>Follow Us</p>
<div class="social-links">
<a href="#instagram" aria-label="Instagram"><i class="bi bi-instagram"></i></a>
</div>
</div>
<div class="footer-links">
<h3>Quick Links</h3>
<ul>
<li><a href="/shop.html">Shop</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/contact.html">Contact</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© 2025 by Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/cart.js"></script>
<script src="/assets/js/shopping.js"></script>
</body>
</html>

245
website/public/product.html Normal file
View File

@@ -0,0 +1,245 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" />
<link rel="stylesheet" href="/assets/css/navbar.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
</head>
<body>
<!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<div id="loading" style="text-align: center; padding: 100px 20px; font-size: 18px; color: #6b7280;">
<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>
<script src="/assets/js/shopping.js"></script>
<script>
async function loadProduct() {
const params = new URLSearchParams(window.location.search);
const productId = params.get('id');
if (!productId) {
document.getElementById('loading').innerHTML = '<p>Product not found</p><a href="/shop.html">Back to Shop</a>';
return;
}
try {
const response = await fetch(\`/api/products/\${productId}\`);
const data = await response.json();
if (!data.success || !data.product) {
throw new Error('Product not found');
}
const product = data.product;
document.title = \`\${product.name} - Sky Art Shop\`;
document.getElementById('productDetail').innerHTML = \`
<div style="font-family: 'Roboto', sans-serif;">
<nav style="background: white; padding: 16px 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<div style="max-width: 1200px; margin: 0 auto; display: flex; align-items: center; gap: 20px;">
<a href="/home.html" style="font-size: 20px; font-weight: 600; color: #1a1a1a; text-decoration: none;">Sky Art Shop</a>
<span style="color: #d1d5db;">/</span>
<a href="/shop.html" style="color: #6b7280; text-decoration: none;">Shop</a>
<span style="color: #d1d5db;">/</span>
<span style="color: #6b7280;">\${product.name}</span>
</div>
</nav>
<div style="max-width: 1200px; margin: 40px auto; padding: 0 24px;">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 60px; margin-bottom: 60px;">
<div>
<div style="background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
<img src="\${product.imageurl || '/assets/images/placeholder.jpg'}"
alt="\${product.name}"
style="width: 100%; height: auto; display: block;"
onerror="this.src='/assets/images/placeholder.jpg'" />
</div>
</div>
<div style="padding: 20px 0;">
<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: #6b46c1; margin: 0;">$\${parseFloat(product.price).toFixed(2)}</p>
\${product.stockquantity > 0 ?
\`<span style="color: #10b981; font-weight: 500;">In Stock (\${product.stockquantity} available)</span>\` :
\`<span style="color: #ef4444; font-weight: 500;">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.description ? \`
<div style="margin-bottom: 32px;">
<h3 style="font-size: 18px; font-weight: 600; color: #1a1a1a; margin-bottom: 12px;">Description</h3>
<p style="color: #6b7280; line-height: 1.7;">\${product.description}</p>
</div>
\` : ''}
\${product.category ? \`
<p style="margin-bottom: 16px;">
<span style="font-weight: 500; color: #6b7280;">Category:</span>
<span style="display: inline-block; margin-left: 8px; padding: 4px 12px; background: #f3f4f6; border-radius: 6px; font-size: 14px;">\${product.category}</span>
</p>
\` : ''}
\${product.color ? \`
<p style="margin-bottom: 24px;">
<span style="font-weight: 500; color: #6b7280;">Color:</span>
<span style="margin-left: 8px;">\${product.color}</span>
</p>
\` : ''}
<div style="display: flex; gap: 12px; margin-top: 32px;">
<button onclick="addToCart()"
style="flex: 1; padding: 16px 32px; background: #6b46c1; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: background 0.2s; display: flex; align-items: center; justify-content: center; gap: 8px;"
onmouseover="this.style.background='#5936a3'"
onmouseout="this.style.background='#6b46c1'">
<i class="bi bi-cart-plus" style="font-size: 20px;"></i>
Add to Cart
</button>
<button onclick="addToWishlist()"
style="width: 56px; padding: 16px; background: transparent; color: #6b46c1; border: 2px solid #6b46c1; border-radius: 8px; font-size: 20px; cursor: pointer; transition: all 0.2s;"
onmouseover="this.style.background='#f3f0ff'"
onmouseout="this.style.background='transparent'">
<i class="bi bi-heart"></i>
</button>
</div>
<a href="/shop.html" style="display: inline-block; margin-top: 24px; color: #6b46c1; text-decoration: none; font-weight: 500;">
<i class="bi bi-arrow-left"></i> Back to Shop
</a>
</div>
</div>
</div>
</div>
\`;
document.getElementById('loading').style.display = 'none';
document.getElementById('productDetail').style.display = 'block';
// Store product data
window.currentProduct = product;
} catch (error) {
console.error('Error loading product:', error);
document.getElementById('loading').innerHTML = '<p style="color: #ef4444;">Error loading product</p><a href="/shop.html" style="color: #6b46c1; text-decoration: none; font-weight: 500;">Back to Shop</a>';
}
}
function addToCart() {
if (window.currentProduct && window.shoppingManager) {
shoppingManager.addToCart(window.currentProduct, 1);
}
}
function addToWishlist() {
if (window.currentProduct && window.shoppingManager) {
shoppingManager.addToWishlist(window.currentProduct);
}
}
loadProduct();
</script>
</body>
</html>

362
website/public/shop.html Normal file
View File

@@ -0,0 +1,362 @@
<!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="Shop all products - Sky Art Shop" />
<title>Shop - 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" />
<link rel="stylesheet" href="/assets/css/navbar.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
</head>
<body> <!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home.html" class="brand-link">
<img src="/uploads/images/8ba675b9-c4e6-41e6-8f14-382b9ee1d019.jpg" 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.html" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/shop.html" class="nav-link">Shop</a></li>
<li class="nav-item"><a href="/portfolio.html" class="nav-link">Portfolio</a></li>
<li class="nav-item"><a href="/about.html" class="nav-link">About</a></li>
<li class="nav-item"><a href="/blog.html" class="nav-link">Blog</a></li>
<li class="nav-item"><a href="/contact.html" 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.html" 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">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout.html" class="btn-primary-full">Proceed to Checkout</a>
<a href="/shop.html" class="btn-text">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.html" class="mobile-link">Home</a></li>
<li><a href="/shop.html" class="mobile-link">Shop</a></li>
<li><a href="/portfolio.html" class="mobile-link">Portfolio</a></li>
<li><a href="/about.html" class="mobile-link">About</a></li>
<li><a href="/blog.html" class="mobile-link">Blog</a></li>
<li><a href="/contact.html" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<!-- Shop Hero -->
<section class="shop-hero">
<div class="container">
<h1>Shop All Products</h1>
<p class="hero-subtitle">
Find everything you need for your creative projects
</p>
</div>
</section>
<!-- Shop Filters -->
<section class="shop-filters">
<div class="container">
<div class="filter-bar">
<div class="filter-group">
<label for="category-filter">Category:</label>
<select id="category-filter">
<option value="">All Products</option>
</select>
</div>
<div class="filter-group">
<label for="sort-filter">Sort By:</label>
<select id="sort-filter">
<option value="name">Name</option>
<option value="price-low">Price: Low to High</option>
<option value="price-high">Price: High to Low</option>
<option value="newest">Newest First</option>
</select>
</div>
</div>
</div>
</section>
<!-- Products Grid -->
<section class="shop-products">
<div class="container">
<div id="loadingMessage" style="text-align: center; padding: 40px">
<p>Loading products...</p>
</div>
<div class="products-grid" id="productsGrid"></div>
<div
id="noProducts"
style="display: none; text-align: center; padding: 40px"
>
<p>No products found.</p>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<h2>Sky Art Shop</h2>
<p>Follow Us</p>
<div class="social-links">
<a href="#instagram" aria-label="Instagram">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor"
>
<path
d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"
/>
</svg>
</a>
</div>
</div>
<div class="footer-links">
<h3>Quick Links</h3>
<ul>
<li><a href="/shop.html">Shop</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/contact.html">Contact</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© 2025 by Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/cart.js"></script>
<script>
let allProducts = [];
let categories = [];
// Load products from API
async function loadProducts() {
try {
const response = await fetch("/api/products");
if (response.ok) {
const data = await response.json();
allProducts = data.products || [];
// Extract unique categories
categories = [
...new Set(allProducts.map((p) => p.category).filter((c) => c)),
];
// Populate category filter
const categoryFilter = document.getElementById("category-filter");
categories.forEach((cat) => {
const option = document.createElement("option");
option.value = cat;
option.textContent = cat;
categoryFilter.appendChild(option);
});
// Display products
displayProducts(allProducts);
} else {
document.getElementById("loadingMessage").style.display = "none";
document.getElementById("noProducts").style.display = "block";
}
} catch (error) {
console.error("Error loading products:", error);
document.getElementById("loadingMessage").innerHTML =
"<p>Error loading products. Please try again later.</p>";
}
}
// Display products
function displayProducts(products) {
const grid = document.getElementById("productsGrid");
const loading = document.getElementById("loadingMessage");
const noProducts = document.getElementById("noProducts");
loading.style.display = "none";
if (products.length === 0) {
grid.innerHTML = "";
noProducts.style.display = "block";
return;
}
noProducts.style.display = "none";
grid.innerHTML = products
.map((product) => {
const imageUrl =
product.imageurl ||
product.images?.[0] ||
"/assets/images/placeholder.jpg";
const price = parseFloat(product.price || 0).toFixed(2);
return `
<div class="product-card">
<a href="/product.html?id=${
product.id
}" class="product-link">
<div class="product-image">
<img src="${imageUrl}" alt="${
product.name
}" loading="lazy" onerror="this.src='/assets/images/placeholder.jpg'" />
</div>
<h3>${product.name}</h3>
${
product.color
? `<span class="product-color-badge">${product.color}</span>`
: ""
}
${
product.shortdescription || product.description
? `<div class="product-description">${
product.shortdescription ||
product.description.substring(0, 100) +
"..."
}</div>`
: ""
}
<p class="price">$${price}</p>
</a>
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
<button class="btn btn-small btn-icon"
onclick="addToWishlist('${
product.id
}', '${product.name.replace(/'/g, "\\'")}', ${
product.price
}, '${imageUrl}')"
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.replace(/'/g, "\\'")}', ${
product.price
}, '${imageUrl}')"
aria-label="Add to cart">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
`;
})
.join("");
}
// Filter and sort products
function filterAndSortProducts() {
const categoryFilter = document.getElementById("category-filter").value;
const sortFilter = document.getElementById("sort-filter").value;
let filtered = allProducts;
// Apply category filter
if (categoryFilter) {
filtered = filtered.filter((p) => p.category === categoryFilter);
}
// Apply sorting
filtered = [...filtered]; // Create copy
switch (sortFilter) {
case "name":
filtered.sort((a, b) => a.name.localeCompare(b.name));
break;
case "price-low":
filtered.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
break;
case "price-high":
filtered.sort((a, b) => parseFloat(b.price) - parseFloat(a.price));
break;
case "newest":
filtered.sort(
(a, b) => new Date(b.createdat) - new Date(a.createdat)
);
break;
}
displayProducts(filtered);
}
// Event listeners
document
.getElementById("category-filter")
.addEventListener("change", filterAndSortProducts);
document
.getElementById("sort-filter")
.addEventListener("change", filterAndSortProducts);
// Initialize
loadProducts();
</script>
<script src="/assets/js/shopping.js"></script>
</body>
</html>