ver first

This commit is contained in:
Flatlogic Bot 2025-11-30 18:12:34 +00:00
parent d2b3b92f20
commit a8dff7dfb0
25 changed files with 1706 additions and 149 deletions

81
admin/add_product.php Normal file
View File

@ -0,0 +1,81 @@
<?php
session_start();
require_once __DIR__ . '/auth_check.php';
$flash_message = $_SESSION['flash_message'] ?? null;
if ($flash_message) {
unset($_SESSION['flash_message']);
}
?>
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>افزودن محصول جدید</title>
<meta name="robots" content="noindex, nofollow">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
<!-- SweetAlert2 -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</head>
<body class="bg-dark text-white">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="font-lalezar">افزودن محصول جدید</h1>
<a href="index.php" class="btn btn-outline-light">بازگشت</a>
</div>
<div class="card bg-dark-2">
<div class="card-body p-4">
<form action="handler.php?action=add" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="name" class="form-label">نام محصول</label>
<input type="text" class="form-control bg-dark text-white" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="description" class="form-label">توضیحات</label>
<textarea class="form-control bg-dark text-white" id="description" name="description" rows="3" required></textarea>
</div>
<div class="mb-3">
<label for="price" class="form-label">قیمت (تومان)</label>
<input type="number" class="form-control bg-dark text-white" id="price" name="price" required>
</div>
<div class="mb-3">
<label for="image" class="form-label">تصویر محصول</label>
<input type="file" class="form-control bg-dark text-white" id="image" name="image" accept="image/*" required>
</div>
<div class="mb-3">
<label for="colors" class="form-label">کدهای رنگ (اختیاری)</label>
<input type="text" class="form-control bg-dark text-white" id="colors" name="colors" placeholder="مثال: #8B4513, #2C2C2C">
<div class="form-text">کدهای رنگ هگزادسیمال را با کاما جدا کنید.</div>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="is_featured" name="is_featured" value="1">
<label class="form-check-label" for="is_featured">محصول ویژه</label>
</div>
<button type="submit" class="btn btn-primary w-100">افزودن محصول</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
<?php if ($flash_message): ?>
Swal.fire({
title: '<?php echo $flash_message["type"] === "success" ? "عالی" : "خطا"; ?>',
html: '<?php echo addslashes($flash_message["message"]); ?>', // Use html to render <br> tags
icon: '<?php echo $flash_message["type"]; ?>',
confirmButtonText: 'باشه'
});
<?php endif; ?>
});
</script>
</body>
</html>

8
admin/auth_check.php Normal file
View File

@ -0,0 +1,8 @@
<?php
session_start();
// Check if the user is logged in. If not, redirect to the login page.
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
header('Location: login.php');
exit;
}

109
admin/edit_product.php Normal file
View File

@ -0,0 +1,109 @@
<?php
session_start();
require_once __DIR__ . '/auth_check.php';
require_once __DIR__ . '/../db/config.php';
$flash_message = $_SESSION['flash_message'] ?? null;
if ($flash_message) {
unset($_SESSION['flash_message']);
}
$product_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($product_id <= 0) {
header('Location: index.php');
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$product_id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) {
header('Location: index.php');
exit;
}
} catch (PDOException $e) {
die("Error fetching product: " . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ویرایش محصول: <?php echo htmlspecialchars($product['name']); ?></title>
<meta name="robots" content="noindex, nofollow">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
<!-- SweetAlert2 -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</head>
<body class="bg-dark text-white">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<h1 class="font-lalezar mb-4">ویرایش محصول</h1>
<div class="card bg-dark-2">
<div class="card-body p-4">
<form action="handler.php?action=edit" method="POST" enctype="multipart/form-data">
<input type="hidden" name="id" value="<?php echo htmlspecialchars($product['id']); ?>">
<input type="hidden" name="current_image" value="<?php echo htmlspecialchars($product['image_url']); ?>">
<div class="mb-3">
<label for="name" class="form-label">نام محصول</label>
<input type="text" class="form-control bg-dark text-white" id="name" name="name" value="<?php echo htmlspecialchars($product['name']); ?>" required>
</div>
<div class="mb-3">
<label for="description" class="form-label">توضیحات</label>
<textarea class="form-control bg-dark text-white" id="description" name="description" rows="3" required><?php echo htmlspecialchars($product['description']); ?></textarea>
</div>
<div class="mb-3">
<label for="price" class="form-label">قیمت (به تومان)</label>
<input type="number" class="form-control bg-dark text-white" id="price" name="price" min="0" value="<?php echo htmlspecialchars($product['price']); ?>" required>
</div>
<div class="mb-3">
<label for="colors" class="form-label">رنگ‌ها</label>
<input type="text" class="form-control bg-dark text-white" id="colors" name="colors" value="<?php echo htmlspecialchars($product['colors'] ?? ''); ?>">
<div class="form-text">رنگ‌های موجود را با کاما از هم جدا کنید (مثال: #FFFFFF, #000000).</div>
</div>
<div class="mb-3">
<label for="image" class="form-label">تصویر محصول</label>
<input type="file" class="form-control bg-dark text-white" id="image" name="image" accept="image/*">
<div class="form-text mt-2">تصویر فعلی:</div>
<img src="../<?php echo htmlspecialchars($product['image_url']); ?>" alt="Current Image" class="img-thumbnail mt-2" width="100">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="is_featured" name="is_featured" value="1" <?php echo ($product['is_featured'] ?? 0) ? 'checked' : ''; ?>>
<label class="form-check-label" for="is_featured">نمایش در محصولات ویژه</label>
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a href="index.php" class="btn btn-secondary">انصراف</a>
<button type="submit" class="btn btn-primary">به‌روزرسانی محصول</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
<?php if ($flash_message): ?>
Swal.fire({
title: '<?php echo $flash_message["type"] === "success" ? "عالی" : "خطا"; ?>',
html: '<?php echo addslashes($flash_message["message"]); ?>', // Use html to render <br> tags
icon: '<?php echo $flash_message["type"]; ?>',
confirmButtonText: 'باشه'
});
<?php endif; ?>
});
</script>
</body>
</html>

169
admin/handler.php Normal file
View File

@ -0,0 +1,169 @@
session_start();
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/auth_check.php';
$action = $_REQUEST['action'] ?? '';
$pdo = db();
// Default redirect location
$redirect_to = 'index.php';
switch ($action) {
case 'add':
$redirect_to = 'add_product.php'; // Redirect back to form on error
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name'] ?? '');
$description = trim($_POST['description'] ?? '');
$price = filter_var($_POST['price'], FILTER_VALIDATE_FLOAT);
$colors = trim($_POST['colors'] ?? '');
$is_featured = isset($_POST['is_featured']) ? 1 : 0;
$errors = [];
// Validation
if (empty($name)) $errors[] = "Product name is required.";
if (empty($description)) $errors[] = "Description is required.";
if ($price === false) $errors[] = "Price is invalid or missing.";
$image_path = '';
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$upload_dir = __DIR__ . '/../assets/images/products/';
if (!is_dir($upload_dir)) {
if (!mkdir($upload_dir, 0777, true)) {
$errors[] = "Image directory does not exist and could not be created.";
}
}
if (!is_writable($upload_dir)) {
$errors[] = "Image directory is not writable. Please check server permissions.";
} else {
$filename = uniqid('product_', true) . '_' . basename($_FILES['image']['name']);
$target_file = $upload_dir . $filename;
if (move_uploaded_file($_FILES['image']['tmp_name'], $target_file)) {
$image_path = 'assets/images/products/' . $filename;
} else {
$errors[] = "Failed to move uploaded file.";
}
}
} else {
$file_error = $_FILES['image']['error'] ?? UPLOAD_ERR_NO_FILE;
$upload_errors = [
UPLOAD_ERR_INI_SIZE => "The uploaded file exceeds the server's maximum upload size (upload_max_filesize).",
UPLOAD_ERR_FORM_SIZE => "The uploaded file exceeds the maximum size specified in the form.",
UPLOAD_ERR_PARTIAL => "The file was only partially uploaded.",
UPLOAD_ERR_NO_FILE => "No file was selected for upload.",
UPLOAD_ERR_NO_TMP_DIR => "Server configuration error: Missing a temporary folder for uploads.",
UPLOAD_ERR_CANT_WRITE => "Server error: Failed to write the uploaded file to disk.",
UPLOAD_ERR_EXTENSION => "A PHP extension prevented the file upload.",
];
$error_message = $upload_errors[$file_error] ?? "An unknown upload error occurred (Code: {$file_error}).";
// Only trigger error if the action is 'add', where image is mandatory
if ($action === 'add') {
$errors[] = "Image Upload Failed: " . $error_message;
}
}
if (empty($errors)) {
try {
$sql = "INSERT INTO products (name, description, price, image_url, colors, is_featured) VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $description, $price, $image_path, $colors, $is_featured]);
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'محصول با موفقیت اضافه شد!'];
$redirect_to = 'index.php';
} catch (PDOException $e) {
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'خطا در افزودن محصول: ' . $e->getMessage()];
}
} else {
$error_message = 'لطفاً تمام خطاها را برطرف کنید:<br><br>' . implode('<br>', $errors);
$_SESSION['flash_message'] = ['type' => 'error', 'message' => $error_message];
}
}
break;
case 'edit':
$id = $_POST['id'] ?? $_GET['id'] ?? null;
$redirect_to = 'edit_product.php?id=' . $id;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = filter_var($id, FILTER_VALIDATE_INT);
$name = trim($_POST['name'] ?? '');
$description = trim($_POST['description'] ?? '');
$price = filter_var($_POST['price'], FILTER_VALIDATE_FLOAT);
$colors = trim($_POST['colors'] ?? '');
$is_featured = isset($_POST['is_featured']) ? 1 : 0;
$errors = [];
if (!$id) {
$errors[] = "شناسه محصول نامعتبر است.";
}
// Other validations...
$image_path = $_POST['current_image'] ?? '';
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$upload_dir = __DIR__ . '/../assets/images/products/';
$filename = uniqid('product_', true) . '_' . basename($_FILES['image']['name']);
$target_file = $upload_dir . $filename;
if (move_uploaded_file($_FILES['image']['tmp_name'], $target_file)) {
if (!empty($image_path) && file_exists(__DIR__ . '/../' . $image_path)) {
unlink(__DIR__ . '/../' . $image_path);
}
$image_path = 'assets/images/products/' . $filename;
} else {
$errors[] = "خطا در آپلود تصویر جدید.";
}
}
if (empty($errors)) {
try {
$sql = "UPDATE products SET name = ?, description = ?, price = ?, image_url = ?, colors = ?, is_featured = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $description, $price, $image_path, $colors, $is_featured, $id]);
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'محصول با موفقیت ویرایش شد!'];
$redirect_to = 'index.php';
} catch (PDOException $e) {
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'خطا در ویرایش محصول: ' . $e->getMessage()];
}
} else {
$error_message = 'فرم دارای خطا است:<br><br>' . implode('<br>', $errors);
$_SESSION['flash_message'] = ['type' => 'error', 'message' => $error_message];
}
}
break;
case 'delete':
$id = filter_var($_GET['id'], FILTER_VALIDATE_INT);
if ($id) {
try {
// First, get the image path to delete the file
$stmt = $pdo->prepare("SELECT image_url FROM products WHERE id = ?");
$stmt->execute([$id]);
$image_to_delete = $stmt->fetchColumn();
// Delete the record
$sql = "DELETE FROM products WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$id]);
// If record deleted, delete the file
if ($stmt->rowCount() > 0 && $image_to_delete && file_exists(__DIR__ . '/../' . $image_to_delete)) {
unlink(__DIR__ . '/../' . $image_to_delete);
}
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'محصول با موفقیت حذف شد.'];
} catch (PDOException $e) {
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'خطا در حذف محصول: ' . $e->getMessage()];
}
} else {
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'شناسه محصول نامعتبر است.'];
}
$redirect_to = 'index.php';
break;
}
// Redirect back after the action
header('Location: ' . $redirect_to);
exit;

117
admin/index.php Normal file
View File

@ -0,0 +1,117 @@
<?php
session_start();
require_once __DIR__ . '/auth_check.php';
require_once __DIR__ . '/../db/config.php';
try {
$pdo = db();
$stmt = $pdo->query("SELECT id, name, price FROM products ORDER BY created_at DESC");
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die("Error fetching products: " . $e->getMessage());
}
$flash_message = $_SESSION['flash_message'] ?? null;
if ($flash_message) {
unset($_SESSION['flash_message']);
}
?>
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>پنل مدیریت - محصولات</title>
<meta name="robots" content="noindex, nofollow">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
<!-- SweetAlert2 -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</head>
<body class="bg-dark text-white">
<div class="container mt-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="font-lalezar">مدیریت محصولات</h1>
<div class="d-flex gap-2">
<a href="add_product.php" class="btn btn-success">+ افزودن محصول جدید</a>
<a href="logout.php" class="btn btn-outline-danger">خروج</a>
</div>
</div>
<div class="table-responsive">
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">نام محصول</th>
<th scope="col">قیمت</th>
<th scope="col">عملیات</th>
</tr>
</thead>
<tbody>
<?php if (empty($products)): ?>
<tr>
<td colspan="4" class="text-center">هیچ محصولی یافت نشد.</td>
</tr>
<?php else: ?>
<?php foreach ($products as $product): ?>
<tr>
<th scope="row"><?php echo htmlspecialchars($product['id']); ?></th>
<td><?php echo htmlspecialchars($product['name']); ?></td>
<td><?php echo number_format($product['price']); ?> تومان</td>
<td>
<a href="edit_product.php?id=<?php echo $product['id']; ?>" class="btn btn-sm btn-primary">ویرایش</a>
<a href="handler.php?action=delete&id=<?php echo $product['id']; ?>" class="btn btn-sm btn-danger delete-btn">حذف</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="mt-4">
<a href="../index.php" class="btn btn-outline-light">بازگشت به سایت</a>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Flash message handling
<?php if ($flash_message): ?>
Swal.fire({
title: '<?php echo $flash_message["type"] === "success" ? "عالی" : "خطا"; ?>',
html: '<?php echo addslashes($flash_message["message"]); ?>',
icon: '<?php echo $flash_message["type"]; ?>',
confirmButtonText: 'باشه'
});
<?php endif; ?>
// Delete confirmation
const deleteButtons = document.querySelectorAll('.delete-btn');
deleteButtons.forEach(button => {
button.addEventListener('click', function (e) {
e.preventDefault();
const href = this.getAttribute('href');
Swal.fire({
title: 'آیا مطمئن هستید؟',
text: "این عمل غیرقابل بازگشت است!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'بله، حذف کن!',
cancelButtonText: 'انصراف'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = href;
}
});
});
});
});
</script>
</body>
</html>

64
admin/login.php Normal file
View File

@ -0,0 +1,64 @@
<?php
session_start();
// If the user is already logged in, redirect them to the admin dashboard
if (isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true) {
header('Location: index.php');
exit;
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// WARNING: This is a highly insecure, hardcoded password for demonstration purposes only.
// In a real-world application, you MUST use a secure, hashed password system.
$hardcoded_password = 'admin123';
if (isset($_POST['password']) && $_POST['password'] === $hardcoded_password) {
// On successful login, set a session variable
$_SESSION['is_admin'] = true;
header('Location: index.php');
exit;
} else {
$error = 'رمز عبور اشتباه است.';
}
}
?>
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ورود به پنل مدیریت</title>
<meta name="robots" content="noindex, nofollow">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="bg-dark text-white">
<div class="container">
<div class="row justify-content-center align-items-center" style="height: 100vh;">
<div class="col-md-4">
<div class="card bg-dark-2">
<div class="card-body p-4">
<h1 class="font-lalezar text-center mb-4">ورود به پنل</h1>
<p class="text-center text-muted mb-4">رمز عبور: admin123</p>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<form method="POST">
<div class="mb-3">
<label for="password" class="form-label">رمز عبور</label>
<input type="password" class="form-control bg-dark text-white" id="password" name="password" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">ورود</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

22
admin/logout.php Normal file
View File

@ -0,0 +1,22 @@
<?php
session_start();
// Unset all of the session variables.
$_SESSION = array();
// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// Finally, destroy the session.
session_destroy();
// Redirect to the login page.
header('Location: login.php');
exit;

261
assets/css/custom.css Normal file
View File

@ -0,0 +1,261 @@
/*
:root variables for the default Light Theme
*/
:root {
--primary-color: #8B4513; /* SaddleBrown */
--secondary-color: #D2B48C; /* Tan */
--accent-color: #C0A080;
--background-color: #FDFBF7;
--surface-color: #FFFFFF; /* For cards, headers, etc. */
--text-color: #4A4A4A;
--heading-color: #2F2F2F;
--border-color: #EAEAEA;
--footer-bg: #2C2C2C;
--white-color: #FFFFFF;
--font-family-sans-serif: 'Montserrat', sans-serif;
--body-font: 'Montserrat', sans-serif;
}
/*
Variables for the Dark Theme
We will apply these by adding a class="dark" to the <html> tag
*/
html.dark {
--primary-color: #C0A080; /* Lighter tan for accents in dark mode */
--secondary-color: #8B4513; /* Darker brown */
--accent-color: #D2B48C;
--background-color: #1A1A1A; /* Very dark grey, almost black */
--surface-color: #2C2C2C; /* Dark grey for cards and surfaces */
--text-color: #D5D5D5; /* Light grey for body text */
--heading-color: #FFFFFF; /* White for headings */
--border-color: #444444; /* Grey for borders */
--footer-bg: #111111; /* Even darker for footer */
}
body {
font-family: var(--body-font);
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.7;
font-weight: 400;
overflow-x: hidden;
transition: background-color 0.3s ease, color 0.3s ease;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 700;
color: var(--heading-color);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
}
a:hover {
color: var(--accent-color);
text-decoration: none;
}
/* --- Buttons --- */
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
color: var(--background-color); /* To have contrast in both themes */
padding: 12px 30px;
font-weight: 600;
border-radius: 50px;
transition: all 0.3s ease-in-out;
}
html.dark .btn-primary {
color: var(--heading-color);
}
.btn-primary:hover, .btn-primary:focus {
background-color: var(--secondary-color);
border-color: var(--secondary-color);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.btn-outline-primary {
border-color: var(--primary-color);
color: var(--primary-color);
padding: 12px 30px;
font-weight: 600;
border-radius: 50px;
}
.btn-outline-primary:hover {
background-color: var(--primary-color);
color: var(--white-color);
}
/* --- Header --- */
.site-header {
background-color: var(--surface-color);
box-shadow: 0 2px 15px rgba(0,0,0,0.05);
border-bottom: 1px solid var(--border-color);
transition: background-color 0.3s ease;
}
html.dark .site-header {
box-shadow: 0 2px 15px rgba(0,0,0,0.2);
}
.site-header .nav-link,
.site-header a {
color: var(--text-color) !important;
font-weight: 500;
font-size: 16px;
padding: 8px 12px;
position: relative;
}
.site-header a.active,
.site-header a:hover {
color: var(--primary-color) !important;
}
.site-header .badge {
background-color: var(--primary-color) !important;
color: var(--background-color) !important;
}
html.dark .site-header .badge {
color: var(--heading-color) !important;
}
/* --- Footer --- */
.site-footer {
background-color: var(--footer-bg);
color: #AFAFAF;
}
.site-footer h5 {
color: var(--heading-color);
}
.site-footer a {
color: #AFAFAF !important;
}
.site-footer a:hover {
color: var(--white-color) !important;
}
.site-footer .border-top {
border-color: var(--border-color) !important;
}
/* --- Product Card --- */
.product-card {
background: var(--surface-color);
border: 1px solid var(--border-color);
border-radius: 15px;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
box-shadow: 0 5px 20px rgba(0,0,0,0.04);
}
html.dark .product-card {
box-shadow: 0 5px 20px rgba(0,0,0,0.15);
}
.product-card:hover {
transform: translateY(-8px);
box-shadow: 0 15px 30px rgba(0,0,0,0.08);
}
html.dark .product-card:hover {
box-shadow: 0 15px 30px rgba(0,0,0,0.25);
}
.product-card .product-image img {
transition: transform 0.5s ease;
}
.product-card:hover .product-image img {
transform: scale(1.05);
}
.product-card .product-info {
padding: 20px;
}
.product-card .product-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 10px;
color: var(--heading-color);
}
.product-card .product-price {
font-size: 1.2rem;
font-weight: 700;
color: var(--primary-color);
}
/* --- Product Page Color Swatches --- */
.color-swatches .btn-check + .btn {
border: 2px solid var(--border-color);
border-radius: 50%;
width: 40px;
height: 40px;
display: inline-block;
padding: 0;
font-size: 0;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.color-swatches .btn-check:checked + .btn {
transform: scale(1.15);
box-shadow: 0 0 0 3px var(--surface-color), 0 0 0 5px var(--primary-color);
}
/* Handle specific colors that need border in light mode */
.color-swatches [style*="#FFFFFF"] + label,
.color-swatches [style*="#ffffff"] + label {
border-color: #dedede;
}
html.dark .color-swatches [style*="#000000"] + label {
border-color: #555;
}
/* --- Admin panel dark theme overrides --- */
.bg-dark {
background-color: var(--background-color) !important;
}
.text-white {
color: var(--text-color) !important;
}
.bg-dark-2 {
background-color: var(--surface-color) !important;
}
.form-control.bg-dark {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
border-color: var(--border-color);
}
.form-control.bg-dark:focus {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
border-color: var(--primary-color);
box-shadow: none;
}
/* --- Product Image Aspect Ratio --- */
.product-image {
overflow: hidden;
position: relative;
background-color: var(--border-color); /* Placeholder bg */
aspect-ratio: 3 / 4; /* Enforce 3:4 aspect ratio */
}
.product-image img {
width: 100%;
height: 100%;
object-fit: cover; /* This will crop the image to fit */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

1
assets/js/main.js Normal file
View File

@ -0,0 +1 @@
// Custom JavaScript will go here

121
cart.php Normal file
View File

@ -0,0 +1,121 @@
<?php
session_start();
require_once 'db/config.php';
$cart_items_detailed = [];
$total_price = 0;
if (!empty($_SESSION['cart'])) {
$cart_item_ids = array_keys($_SESSION['cart']);
// Extract pure product IDs from the composite key (e.g., '1-Black' -> '1')
$product_ids = array_map(function($id) {
return (int)explode('-', $id)[0];
}, $cart_item_ids);
if (!empty($product_ids)) {
$placeholders = implode(',', array_fill(0, count($product_ids), '?'));
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id, name, price, image_url FROM products WHERE id IN ($placeholders)");
$stmt->execute(array_unique($product_ids));
$products_data = $stmt->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_UNIQUE);
foreach ($_SESSION['cart'] as $cart_item_id => $item) {
$product_id = (int)explode('-', $cart_item_id)[0];
if (isset($products_data[$product_id])) {
$product = $products_data[$product_id];
$quantity = $item['quantity'];
$color = $item['color'];
$subtotal = $product['price'] * $quantity;
$total_price += $subtotal;
$cart_items_detailed[] = [
'cart_item_id' => $cart_item_id,
'product_id' => $product_id,
'name' => $product['name'],
'price' => $product['price'],
'image_url' => $product['image_url'],
'quantity' => $quantity,
'color' => $color,
'subtotal' => $subtotal
];
}
}
} catch (PDOException $e) {
error_log("DB Error: " . $e->getMessage());
$cart_items_detailed = [];
$total_price = 0;
}
}
}
$page_title = 'سبد خرید';
include 'includes/header.php';
?>
<div class="text-center mb-5">
<h1 class="display-4 fw-bold">سبد خرید شما</h1>
</div>
<?php if (empty($cart_items_detailed)): ?>
<div class="text-center p-5 bg-light rounded-3">
<p class="lead">سبد خرید شما خالی است.</p>
<a href="shop.php" class="btn btn-primary">بازگشت به فروشگاه</a>
</div>
<?php else: ?>
<form action="cart_handler.php" method="POST">
<input type="hidden" name="update_cart" value="1">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th scope="col" colspan="2">محصول</th>
<th scope="col" class="text-center">قیمت</th>
<th scope="col" class="text-center">تعداد</th>
<th scope="col" class="text-end">جمع کل</th>
<th scope="col" class="text-center">حذف</th>
</tr>
</thead>
<tbody>
<?php foreach ($cart_items_detailed as $item): ?>
<tr>
<td style="width: 100px;">
<img src="<?php echo htmlspecialchars($item['image_url']); ?>" alt="<?php echo htmlspecialchars($item['name']); ?>" class="img-fluid rounded-3">
</td>
<td>
<h5 class="mb-0"><?php echo htmlspecialchars($item['name']); ?></h5>
<?php if ($item['color']): ?>
<small class="text-muted">رنگ: <?php echo htmlspecialchars($item['color']); ?></small>
<?php endif; ?>
</td>
<td class="text-center"><strong><?php echo number_format($item['price']); ?></strong></td>
<td class="text-center" style="width: 120px;">
<input type="number" class="form-control text-center" name="quantities[<?php echo $item['cart_item_id']; ?>]" value="<?php echo $item['quantity']; ?>" min="1" max="10">
</td>
<td class="text-end"><strong><?php echo number_format($item['subtotal']); ?></strong></td>
<td class="text-center">
<a href="cart_handler.php?action=remove&id=<?php echo $item['cart_item_id']; ?>" class="btn btn-sm btn-outline-danger">&times;</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="d-flex justify-content-between align-items-center mt-4 flex-wrap gap-3">
<button type="submit" class="btn btn-outline-secondary">به‌روزرسانی سبد</button>
<div class="text-end">
<h4>جمع نهایی: <span class="fw-bold text-primary"><?php echo number_format($total_price); ?> تومان</span></h4>
</div>
</div>
</form>
<div class="text-center mt-5">
<a href="checkout.php" class="btn btn-primary btn-lg">ادامه جهت تسویه حساب</a>
</div>
<?php endif; ?>
<?php include 'includes/footer.php'; ?>

70
cart_handler.php Normal file
View File

@ -0,0 +1,70 @@
<?php
session_start();
// Initialize the cart if it doesn't exist
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = [];
}
// Check if the form was submitted and it's an add-to-cart action
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_to_cart'])) {
$product_id = isset($_POST['product_id']) ? (int)$_POST['product_id'] : 0;
$quantity = isset($_POST['quantity']) ? (int)$_POST['quantity'] : 1;
$color = isset($_POST['color']) ? trim($_POST['color']) : null;
if ($product_id > 0 && $quantity > 0) {
// Create a unique ID for the cart item based on product ID and color
$cart_item_id = $product_id . ($color ? '-' . preg_replace('/[^a-zA-Z0-9_]/ ', '-', $color) : '');
// If the exact item (product + color) is already in the cart, update the quantity
if (isset($_SESSION['cart'][$cart_item_id])) {
$_SESSION['cart'][$cart_item_id]['quantity'] += $quantity;
} else {
// Otherwise, add it as a new item
$_SESSION['cart'][$cart_item_id] = [
'product_id' => $product_id,
'quantity' => $quantity,
'color' => $color
];
}
}
// Redirect to the cart page to show the updated cart
header('Location: cart.php');
exit;
}
// Handle removing an item from the cart
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'remove') {
$cart_item_id = isset($_GET['id']) ? $_GET['id'] : '';
if (!empty($cart_item_id) && isset($_SESSION['cart'][$cart_item_id])) {
unset($_SESSION['cart'][$cart_item_id]);
}
// Redirect back to the cart page
header('Location: cart.php');
exit;
}
// Handle updating quantities
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_cart'])){
if(!empty($_POST['quantities'])){
foreach($_POST['quantities'] as $cart_item_id => $quantity){
$quantity = (int)$quantity;
if(!empty($cart_item_id) && isset($_SESSION['cart'][$cart_item_id])){
if($quantity > 0){
$_SESSION['cart'][$cart_item_id]['quantity'] = $quantity;
} else {
// Remove item if quantity is 0 or less
unset($_SESSION['cart'][$cart_item_id]);
}
}
}
}
header('Location: cart.php');
exit;
}
// If someone accesses this file directly without a valid action, redirect them to the shop.
header('Location: shop.php');
exit;

174
checkout.php Normal file
View File

@ -0,0 +1,174 @@
<?php
session_start();
require_once 'db/config.php';
// If cart is empty, redirect to shop page, there is nothing to checkout
if (empty($_SESSION['cart'])) {
header('Location: shop.php');
exit;
}
$p_title = "تسویه حساب";
$order_placed_successfully = false;
$error_message = '';
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// --- Data Validation ---
$name = trim($_POST['customer_name'] ?? '');
$email = trim($_POST['customer_email'] ?? '');
$address = trim($_POST['customer_address'] ?? '');
if (empty($name) || empty($email) || empty($address)) {
$error_message = 'لطفاً تمام فیلدها را پر کنید.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error_message = 'لطفاً یک آدرس ایمیل معتبر وارد کنید.';
}
if(empty($error_message)) {
$pdo = db();
try {
// --- Server-side recalculation of total ---
$product_ids = array_keys($_SESSION['cart']);
$placeholders = implode(',', array_fill(0, count($product_ids), '?'));
$stmt = $pdo->prepare("SELECT id, price FROM products WHERE id IN ($placeholders)");
$stmt->execute($product_ids);
$products_from_db = $stmt->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_UNIQUE);
$total_amount = 0;
foreach ($_SESSION['cart'] as $product_id => $quantity) {
if(isset($products_from_db[$product_id])){
$total_amount += $products_from_db[$product_id]['price'] * $quantity;
}
}
// --- Database Transaction ---
$pdo->beginTransaction();
// 1. Insert into orders table
$sql_order = "INSERT INTO orders (customer_name, customer_email, customer_address, total_amount) VALUES (?, ?, ?, ?)";
$stmt_order = $pdo->prepare($sql_order);
$stmt_order->execute([$name, $email, $address, $total_amount]);
$order_id = $pdo->lastInsertId();
// 2. Insert into order_items table
$sql_items = "INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?)";
$stmt_items = $pdo->prepare($sql_items);
foreach ($_SESSION['cart'] as $product_id => $quantity) {
if(isset($products_from_db[$product_id])){
$price = $products_from_db[$product_id]['price'];
$stmt_items->execute([$order_id, $product_id, $quantity, $price]);
}
}
// 3. Commit the transaction
$pdo->commit();
// 4. Clear the cart and set success flag
unset($_SESSION['cart']);
$order_placed_successfully = true;
$p_title = "سفارش شما ثبت شد";
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
error_log("Checkout Error: " . $e->getMessage());
$error_message = 'مشکلی در ثبت سفارش شما به وجود آمد. لطفاً دوباره تلاش کنید.';
}
}
}
?>
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $p_title; ?> - چرم آتیمه</title>
<meta name="robots" content="noindex, nofollow">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Inter:wght@400;500&family=Lalezar&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="bg-dark text-white">
<!-- Header -->
<header class="p-3 mb-3 border-bottom border-secondary">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a href="index.php" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
<h1 class="font-playfair fs-2" style="color: #D4AF37;">آتیمه</h1>
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="index.php" class="nav-link px-2 text-white">خانه</a></li>
<li><a href="shop.php" class="nav-link px-2 text-white">فروشگاه</a></li>
</ul>
<div class="text-end">
<a href="cart.php" class="btn btn-outline-warning position-relative">
سبد خرید
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
<?php echo count($_SESSION['cart'] ?? []); ?>
</span>
</a>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container my-5">
<div class="text-center mb-5">
<h2 class="font-lalezar display-4"><?php echo $p_title; ?></h2>
</div>
<div class="row justify-content-center">
<div class="col-md-8">
<?php if ($order_placed_successfully): ?>
<div class="alert alert-success text-center">
<h4>از خرید شما متشکریم!</h4>
<p>سفارش شما با موفقیت ثبت شد و به زودی پردازش خواهد شد. یک ایمیل تایید برای شما ارسال گردید.</p>
<a href="shop.php" class="btn btn-warning">بازگشت به فروشگاه</a>
</div>
<?php else: ?>
<?php if (!empty($error_message)): ?>
<div class="alert alert-danger">.<?php echo $error_message; ?></div>
<?php endif; ?>
<div class="card bg-dark-2">
<div class="card-body p-4">
<h5 class="card-title mb-4">اطلاعات ارسال</h5>
<form action="checkout.php" method="POST">
<div class="mb-3">
<label for="customer_name" class="form-label">نام و نام خانوادگی</label>
<input type="text" class="form-control bg-dark text-white" id="customer_name" name="customer_name" required>
</div>
<div class="mb-3">
<label for="customer_email" class="form-label">آدرس ایمیل</label>
<input type="email" class="form-control bg-dark text-white" id="customer_email" name="customer_email" required>
</div>
<div class="mb-3">
<label for="customer_address" class="form-label">آدرس کامل</label>
<textarea class="form-control bg-dark text-white" id="customer_address" name="customer_address" rows="3" required></textarea>
</div>
<div class="d-grid mt-4">
<button type="submit" class="btn btn-warning btn-lg fw-bold">ثبت سفارش و پرداخت</button>
</div>
</form>
</div>
</div>
<?php endif; ?>
</div>
</div>
</main>
<!-- Footer -->
<footer class="py-5 mt-5 border-top border-secondary">
<div class="container text-center">
<p class="text-muted">&copy; <?php echo date("Y"); ?> چرم آتیمه. تمام حقوق محفوظ است.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

87
contact.php Normal file
View File

@ -0,0 +1,87 @@
<?php
session_start();
require_once __DIR__ . '/mail/MailService.php';
$page_title = 'تماس با ما';
$page_description = 'با ما در تماس باشید. نظرات و پیشنهادات شما برای ما ارزشمند است.';
$message = '';
$error = '';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$subject = trim($_POST['subject']);
$body = trim($_POST['message']);
if (empty($name) || empty($email) || empty($subject) || empty($body) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = 'لطفاً تمام فیلدها را به درستی پر کنید.';
} else {
$response = MailService::sendContactMessage($name, $email, $body, null, $subject);
if (!empty($response['success'])) {
$message = 'پیام شما با موفقیت ارسال شد. سپاسگزاریم!';
} else {
$error = 'خطایی در ارسال پیام رخ داد. لطفاً بعداً تلاش کنید. متن خطا: ' . htmlspecialchars($response['error'] ?? 'Unknown error');
}
}
}
include 'includes/header.php';
?>
<main class="container my-5 py-5">
<div class="row justify-content-center">
<div class="col-lg-8 text-center" data-aos="fade-up">
<h1 class="display-4 fw-bold"><?php echo $page_title; ?></h1>
<p class="lead text-white-50 mt-3"><?php echo $page_description; ?></p>
</div>
</div>
<div class="row justify-content-center mt-5">
<div class="col-lg-8">
<div class="card border-0" style="background-color: var(--surface-color);">
<div class="card-body p-4 p-md-5">
<?php if ($message): ?>
<div class="alert alert-success" role="alert">
<?php echo $message; ?>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger" role="alert">
<?php echo $error; ?>
</div>
<?php endif; ?>
<form action="contact.php" method="POST" data-aos="fade-up" data-aos-delay="200">
<div class="mb-4">
<label for="name" class="form-label fs-5">نام شما</label>
<input type="text" class="form-control form-control-lg bg-dark text-white" id="name" name="name" required>
</div>
<div class="mb-4">
<label for="email" class="form-label fs-5">ایمیل شما</label>
<input type="email" class="form-control form-control-lg bg-dark text-white" id="email" name="email" required>
</div>
<div class="mb-4">
<label for="subject" class="form-label fs-5">موضوع</label>
<input type="text" class="form-control form-control-lg bg-dark text-white" id="subject" name="subject" required>
</div>
<div class="mb-4">
<label for="message" class="form-label fs-5">پیام شما</label>
<textarea class="form-control form-control-lg bg-dark text-white" id="message" name="message" rows="5" required></textarea>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">ارسال پیام</button>
</div>
</form>
</div>
</div>
<div class="alert alert-info mt-4"><b>توجه:</b> این فرم برای اهداف آزمایشی است. برای دریافت واقعی ایمیل‌ها، باید اطلاعات سرور ایمیل (SMTP) خود را در فایل <code>.env</code> وارد کنید.</div>
</div>
</div>
</main>
<?php include 'includes/footer.php'; ?>
</body>
</html>

View File

@ -0,0 +1 @@
ALTER TABLE products ADD COLUMN colors VARCHAR(255) DEFAULT NULL COMMENT 'Comma-separated list of available colors';

View File

@ -0,0 +1 @@
ALTER TABLE products ADD COLUMN is_featured BOOLEAN DEFAULT 0;

58
includes/footer.php Normal file
View File

@ -0,0 +1,58 @@
</main> <!-- close main container -->
<footer class="site-footer mt-5 py-5">
<div class="container">
<div class="row">
<div class="col-lg-4 col-md-6 mb-4 mb-lg-0">
<h5 class="fw-bold mb-3">آتیمه</h5>
<p class="text-white-50">تجربه اصالت و کیفیت در محصولات چرمی دست‌دوز. ما به هنر و ماندگاری اعتقاد داریم.</p>
</div>
<div class="col-lg-2 col-md-6 mb-4 mb-lg-0">
<h5 class="fw-bold mb-3">دسترسی سریع</h5>
<ul class="list-unstyled">
<li><a href="shop.php" class="text-white-50">فروشگاه</a></li>
<li><a href="#" class="text-white-50">درباره ما</a></li>
<li><a href="#" class="text-white-50">قوانین و مقررات</a></li>
<li><a href="#" class="text-white-50">سوالات متداول</a></li>
</ul>
</div>
<div class="col-lg-3 col-md-6 mb-4 mb-lg-0">
<h5 class="fw-bold mb-3">تماس با ما</h5>
<ul class="list-unstyled">
<li class="d-flex align-items-center mb-2">
<i class="bi bi-geo-alt-fill me-2 text-primary"></i>
<span class="text-white-50">تهران، خیابان هنر، پلاک ۱۲</span>
</li>
<li class="d-flex align-items-center mb-2">
<i class="bi bi-telephone-fill me-2 text-primary"></i>
<a href="tel:+982122334455" class="text-white-50">۰۲۱-۲۲۳۳۴۴۵۵</a>
</li>
<li class="d-flex align-items-center">
<i class="bi bi-envelope-fill me-2 text-primary"></i>
<a href="mailto:info@atimeh.com" class="text-white-50">info@atimeh.com</a>
</li>
</ul>
</div>
<div class="col-lg-3 col-md-6">
<h5 class="fw-bold mb-3">ما را دنبال کنید</h5>
<p class="text-white-50">از جدیدترین محصولات و تخفیف‌ها باخبر شوید.</p>
<div class="d-flex mt-3">
<a href="#" class="btn btn-outline-primary me-2"><i class="bi bi-instagram"></i></a>
<a href="#" class="btn btn-outline-primary me-2"><i class="bi bi-telegram"></i></a>
<a href="#" class="btn btn-outline-primary"><i class="bi bi-whatsapp"></i></a>
</div>
</div>
</div>
<div class="text-center text-white-50 pt-4 mt-4 border-top border-secondary">
<p>&copy; <?php echo date("Y"); ?> تمام حقوق برای چرم آتیمه محفوظ است.</p>
</div>
</div>
</footer>
<!-- Bootstrap JS Bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- Custom JS -->
<script src="/assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

79
includes/header.php Normal file
View File

@ -0,0 +1,79 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$cart_item_count = isset($_SESSION['cart']) ? array_sum(array_column($_SESSION['cart'], 'quantity')) : 0;
$page_title = $page_title ?? 'فروشگاه آتیمه'; // Default title
?>
<!DOCTYPE html>
<html lang="fa" dir="rtl" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($page_title); ?></title>
<meta name="description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'خرید محصولات چرمی لوکس و با کیفیت.'); ?>">
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700&display=swap" rel="stylesheet">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<!-- AOS CSS -->
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<!-- Custom CSS -->
<link rel="stylesheet" href="/assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<header class="site-header sticky-top py-3">
<nav class="navbar navbar-expand-lg container">
<div class="container-fluid">
<a class="navbar-brand fw-bold fs-4" href="index.php">آتیمه</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavbar" aria-controls="mainNavbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainNavbar">
<ul class="navbar-nav mx-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="index.php">خانه</a>
</li>
<li class="nav-item">
<a class="nav-link" href="shop.php">فروشگاه</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">درباره ما</a>
</li>
<li class="nav-item">
<a class="nav-link" href="contact.php">تماس با ما</a>
</li>
</ul>
<div class="d-flex align-items-center">
<a href="cart.php" class="ms-4 position-relative">
<i class="bi bi-bag fs-5"></i>
<?php if ($cart_item_count > 0): ?>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
<?php echo $cart_item_count; ?>
<span class="visually-hidden">محصول در سبد خرید</span>
</span>
<?php endif; ?>
</a>
<a href="/admin/login.php" class="ms-3">
<i class="bi bi-person fs-5"></i>
</a>
</div>
</div>
</div>
</nav>
</header>
<main>

25
includes/pexels.php Normal file
View File

@ -0,0 +1,25 @@
<?php
function pexels_key() {
$k = getenv('PEXELS_KEY');
return $k && strlen($k) > 0 ? $k : 'Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18';
}
function pexels_get($url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [ 'Authorization: '. pexels_key() ],
CURLOPT_TIMEOUT => 15,
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code >= 200 && $code < 300 && $resp) return json_decode($resp, true);
return null;
}
function download_to($srcUrl, $destPath) {
$data = file_get_contents($srcUrl);
if ($data === false) return false;
if (!is_dir(dirname($destPath))) mkdir(dirname($destPath), 0775, true);
return file_put_contents($destPath, $data) !== false;
}

230
index.php
View File

@ -1,150 +1,88 @@
<?php <?php
declare(strict_types=1); $page_title = 'صفحه اصلی';
@ini_set('display_errors', '1'); include 'includes/header.php';
@error_reporting(E_ALL); ?>
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION; <!-- Hero Section -->
$now = date('Y-m-d H:i:s'); <section class="hero-section vh-100 d-flex align-items-center text-white text-center">
?> <div class="video-background-wrapper">
<!doctype html> <div class="video-overlay"></div>
<html lang="en"> <video playsinline="playsinline" autoplay="autoplay" muted="muted" loop="loop">
<head> <source src="https://storage.googleapis.com/gemini-agent-mediabucket-prod/v-001/video_bg.mp4" type="video/mp4">
<meta charset="utf-8" /> </video>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div> </div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> <div class="container position-relative">
<p class="hint">This page will update automatically as the plan is implemented.</p> <h1 class="display-3 fw-bold mb-3 hero-title" data-aos="fade-up">اصالت در هر نگاه</h1>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p> <p class="lead fs-4 mb-4 hero-subtitle" data-aos="fade-up" data-aos-delay="200">محصولات چرمی دست‌دوز، آفریده برای ماندگاری.</p>
<a href="shop.php" class="btn btn-primary btn-lg" data-aos="fade-up" data-aos-delay="400">کاوش در مجموعه</a>
</div> </div>
</main> </section>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer> <!-- Featured Products Section -->
</body> <section id="featured-products" class="py-5">
</html> <div class="container">
<div class="text-center mb-5" data-aos="fade-up">
<h2 class="display-5 fw-bold">مجموعه برگزیده ما</h2>
<p class="text-white-50 fs-5">دست‌چین شده برای سلیقه‌های خاص.</p>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 g-lg-5">
<?php
require_once 'db/config.php';
try {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM products WHERE is_featured = 1 ORDER BY created_at DESC LIMIT 3");
$featured_products = $stmt->fetchAll(PDO::FETCH_ASSOC);
$animations = ['fade-up', 'zoom-in-up', 'fade-left'];
if (empty($featured_products)) {
echo '<div class="col-12"><p class="text-center text-white-50">هیچ محصولی برای نمایش وجود ندارد.</p></div>';
} else {
$delay = 0;
foreach ($featured_products as $key => $product) {
$animation = $animations[$key % count($animations)]; // Cycle through animations
echo '<div class="col" data-aos="' . $animation . '" data-aos-delay="' . $delay . '">';
echo ' <div class="product-card h-100">';
echo ' <div class="product-image">';
echo ' <a href="product.php?id=' . $product['id'] . '">';
echo ' <img src="' . htmlspecialchars($product['image_url']) . '" class="img-fluid" alt="' . htmlspecialchars($product['name']) . '">';
echo ' </a>';
echo ' </div>';
echo ' <div class="product-info text-center">';
echo ' <h3 class="product-title"><a href="product.php?id=' . $product['id'] . '" class="text-decoration-none">' . htmlspecialchars($product['name']) . '</a></h3>';
echo ' <p class="product-price">' . number_format($product['price']) . ' تومان</p>';
echo ' </div>';
echo ' </div>';
echo '</div>';
$delay += 150;
}
}
} catch (PDOException $e) {
error_log("Database error: " . $e->getMessage());
echo '<div class="col-12"><p class="text-center text-danger">خطا در بارگذاری محصولات.</p></div>';
}
?>
</div>
<div class="text-center mt-5" data-aos="fade-up">
<a href="shop.php" class="btn btn-outline-gold btn-lg">مشاهده تمام محصولات</a>
</div>
</div>
</section>
<!-- About Us Section -->
<section id="about-us" class="py-5 my-5">
<div class="container">
<div class="row align-items-center">
<div class="col-md-6" data-aos="fade-right">
<img src="https://storage.googleapis.com/gemini-agent-mediabucket-prod/v-001/about-us.jpg" alt="درباره ما" class="img-fluid rounded-4 shadow-lg">
</div>
<div class="col-md-6 mt-4 mt-md-0 ps-md-5" data-aos="fade-left">
<h2 class="display-5 fw-bold">داستان آتیمه</h2>
<p class="text-white-50 fs-5 mt-3">ما در آتیمه، به تلفیق هنر سنتی و طراحی مدرن باور داریم. هر محصول، حاصل ساعت‌ها کار دست هنرمندان ماهر و استفاده از بهترین چرم‌های طبیعی است. هدف ما خلق آثاری است که نه تنها یک وسیله، بلکه بخشی از داستان و استایل شما باشند.</p>
<a href="#" class="btn btn-primary mt-3">بیشتر بدانید</a>
</div>
</div>
</div>
</section>
<?php include 'includes/footer.php'; ?>

54
migrate.php Normal file
View File

@ -0,0 +1,54 @@
<?php
require_once 'db/config.php';
// Simple migration runner
function run_migrations() {
$pdo = db();
$migrations_dir = __DIR__ . '/db/migrations';
// Create migrations table if it doesn't exist
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (migration VARCHAR(255) PRIMARY KEY, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
// Get executed migrations
$executed_migrations_stmt = $pdo->query("SELECT migration FROM migrations");
$executed_migrations = $executed_migrations_stmt->fetchAll(PDO::FETCH_COLUMN);
$migration_files = glob($migrations_dir . '/*.sql');
sort($migration_files);
foreach ($migration_files as $file) {
$migration_name = basename($file);
if (in_array($migration_name, $executed_migrations)) {
continue; // Skip already executed migration
}
echo "Running migration: {$migration_name}...
";
$sql = file_get_contents($file);
try {
$pdo->exec($sql);
// Log the migration
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
$stmt->execute([$migration_name]);
echo "Migration {$migration_name} executed successfully.
";
} catch (PDOException $e) {
echo "Error running migration {$migration_name}: " . $e->getMessage() . "
";
// Stop on error
return false;
}
}
echo "All new migrations have been executed.
";
return true;
}
// Run it
run_migrations();

71
product.php Normal file
View File

@ -0,0 +1,71 @@
<?php
session_start();
require_once 'db/config.php';
$product_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($product_id <= 0) {
header("Location: shop.php");
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$product_id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) {
header("Location: shop.php");
exit;
}
} catch (PDOException $e) {
error_log("DB Error: " . $e->getMessage());
die("An error occurred. Please try again later.");
}
$page_title = htmlspecialchars($product['name']);
$available_colors = !empty($product['colors']) ? array_map('trim', explode(',', $product['colors'])) : [];
include 'includes/header.php';
?>
<div class="row g-5">
<div class="col-lg-6">
<img src="<?php echo htmlspecialchars($product['image_url']); ?>" class="img-fluid rounded-4 shadow-lg w-100" alt="<?php echo htmlspecialchars($product['name']); ?>" style="aspect-ratio: 1/1; object-fit: cover;">
</div>
<div class="col-lg-6 d-flex flex-column justify-content-center">
<h1 class="display-4 fw-bold"><?php echo htmlspecialchars($product['name']); ?></h1>
<p class="lead text-white-50 my-3"><?php echo htmlspecialchars($product['description']); ?></p>
<div class="display-5 fw-bold my-4 text-gold"><?php echo number_format($product['price']); ?> <span class="fs-5 text-white-50">تومان</span></div>
<form action="cart_handler.php" method="POST">
<input type="hidden" name="product_id" value="<?php echo $product['id']; ?>">
<?php if (!empty($available_colors)): ?>
<div class="mb-4">
<label class="form-label fw-bold fs-5 mb-3">انتخاب رنگ:</label>
<div class="d-flex flex-wrap gap-3 color-swatches">
<?php foreach ($available_colors as $index => $color): ?>
<div data-bs-toggle="tooltip" title="<?php echo htmlspecialchars($color); ?>">
<input type="radio" class="btn-check" name="color" id="color-<?php echo $index; ?>" value="<?php echo htmlspecialchars($color); ?>" autocomplete="off" <?php echo $index === 0 ? 'checked' : ''; ?>>
<label class="btn" for="color-<?php echo $index; ?>"><?php echo htmlspecialchars($color); ?></label>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div class="d-flex align-items-center mb-4">
<label for="quantity" class="form-label ms-3 mb-0 fs-5">تعداد:</label>
<input type="number" name="quantity" id="quantity" class="form-control bg-dark text-white" value="1" min="1" max="10" style="width: 80px;">
</div>
<button type="submit" name="add_to_cart" class="btn btn-primary btn-lg w-100 py-3 fw-bold">افزودن به سبد خرید</button>
</form>
</div>
</div>
<?php include 'includes/footer.php'; ?>

46
shop.php Normal file
View File

@ -0,0 +1,46 @@
<?php
require_once 'db/config.php';
try {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM products ORDER BY created_at DESC");
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("DB Error: " . $e->getMessage());
$products = [];
}
$page_title = 'فروشگاه';
include 'includes/header.php';
?>
<div class="text-center mb-5">
<h1 class="display-4 fw-bold">گالری محصولات</h1>
<p class="lead text-muted">دست‌سازه‌هایی از چرم طبیعی، با عشق و دقت</p>
</div>
<div class="row row-cols-1 row-cols-sm-2 row-cols-lg-3 row-cols-xl-4 g-4">
<?php if (!empty($products)): ?>
<?php foreach ($products as $product): ?>
<div class="col">
<div class="product-card h-100">
<div class="product-image">
<a href="product.php?id=<?php echo $product['id']; ?>">
<img src="<?php echo htmlspecialchars($product['image_url']); ?>" class="img-fluid" alt="<?php echo htmlspecialchars($product['name']); ?>">
</a>
</div>
<div class="product-info text-center">
<h3 class="product-title"><a href="product.php?id=<?php echo $product['id']; ?>" class="text-decoration-none"><?php echo htmlspecialchars($product['name']); ?></a></h3>
<p class="product-price"><?php echo number_format($product['price']); ?> تومان</p>
</div>
</div>
</div>
<?php endforeach; ?>
<?php else: ?>
<div class="col-12">
<p class="text-center p-5 bg-light rounded-3">محصولی برای نمایش یافت نشد.</p>
</div>
<?php endif; ?>
</div>
<?php include 'includes/footer.php'; ?>