ver first
This commit is contained in:
parent
d2b3b92f20
commit
a8dff7dfb0
81
admin/add_product.php
Normal file
81
admin/add_product.php
Normal 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
8
admin/auth_check.php
Normal 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
109
admin/edit_product.php
Normal 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
169
admin/handler.php
Normal 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
117
admin/index.php
Normal 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
64
admin/login.php
Normal 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
22
admin/logout.php
Normal 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
261
assets/css/custom.css
Normal 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 */
|
||||
}
|
||||
BIN
assets/images/products/new_leather_product_1.jpg
Normal file
BIN
assets/images/products/new_leather_product_1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/images/products/new_leather_product_2.jpg
Normal file
BIN
assets/images/products/new_leather_product_2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
assets/images/products/new_leather_product_3.jpg
Normal file
BIN
assets/images/products/new_leather_product_3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
1
assets/js/main.js
Normal file
1
assets/js/main.js
Normal file
@ -0,0 +1 @@
|
||||
// Custom JavaScript will go here
|
||||
121
cart.php
Normal file
121
cart.php
Normal 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">×</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
70
cart_handler.php
Normal 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
174
checkout.php
Normal 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">© <?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
87
contact.php
Normal 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>
|
||||
1
db/migrations/001_add_colors_to_products.sql
Normal file
1
db/migrations/001_add_colors_to_products.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE products ADD COLUMN colors VARCHAR(255) DEFAULT NULL COMMENT 'Comma-separated list of available colors';
|
||||
1
db/migrations/002_add_is_featured_to_products.sql
Normal file
1
db/migrations/002_add_is_featured_to_products.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE products ADD COLUMN is_featured BOOLEAN DEFAULT 0;
|
||||
58
includes/footer.php
Normal file
58
includes/footer.php
Normal 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>© <?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
79
includes/header.php
Normal 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
25
includes/pexels.php
Normal 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;
|
||||
}
|
||||
236
index.php
236
index.php
@ -1,150 +1,88 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
<?php
|
||||
$page_title = 'صفحه اصلی';
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
<!-- Hero Section -->
|
||||
<section class="hero-section vh-100 d-flex align-items-center text-white text-center">
|
||||
<div class="video-background-wrapper">
|
||||
<div class="video-overlay"></div>
|
||||
<video playsinline="playsinline" autoplay="autoplay" muted="muted" loop="loop">
|
||||
<source src="https://storage.googleapis.com/gemini-agent-mediabucket-prod/v-001/video_bg.mp4" type="video/mp4">
|
||||
</video>
|
||||
</div>
|
||||
<div class="container position-relative">
|
||||
<h1 class="display-3 fw-bold mb-3 hero-title" data-aos="fade-up">اصالت در هر نگاه</h1>
|
||||
<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>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- Featured Products Section -->
|
||||
<section id="featured-products" class="py-5">
|
||||
<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
54
migrate.php
Normal 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
71
product.php
Normal 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
46
shop.php
Normal 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'; ?>
|
||||
Loading…
x
Reference in New Issue
Block a user