3 ver
This commit is contained in:
parent
f745cb0f27
commit
20dd5c8f61
61
about.php
Normal file
61
about.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
$page_title = 'درباره ما';
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main class="container py-5">
|
||||||
|
<div class="text-center mb-5" data-aos="fade-down">
|
||||||
|
<h1 class="display-4 fw-bold">داستان آتیمه</h1>
|
||||||
|
<p class="fs-5 text-muted">تلفیق هنر سنتی و طراحی مدرن</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<div class="card bg-dark-2 border-0 shadow-lg rounded-4 overflow-hidden">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-lg-6" data-aos="fade-right">
|
||||||
|
<img src="assets/images/pexels/about-us-34942790.jpg" class="img-fluid h-100" alt="هنر چرمدوزی" style="object-fit: cover; min-height: 400px;">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 d-flex align-items-center" data-aos="fade-left">
|
||||||
|
<div class="card-body p-4 p-md-5">
|
||||||
|
<h2 class="card-title fw-bold mb-3">باور ما</h2>
|
||||||
|
<p class="fs-5 lh-lg">ما در آتیمه، به قدرت دستها و اصالت مواد اولیه باور داریم. داستان ما از یک کارگاه کوچک و عشقی عمیق به هنر چرمدوزی آغاز شد. هدف ما خلق آثاری است که نه تنها یک وسیله کاربردی، بلکه بخشی از داستان و استایل روزمره شما باشند؛ آثاری که با گذر زمان، زیباتر و شخصیتر میشوند.</p>
|
||||||
|
<p class="fs-5 lh-lg mt-3">هر محصول، حاصل ساعتها کار دست هنرمندان ماهر و استفاده از بهترین و باکیفیتترین چرمهای طبیعی است. ما به جزئیات اهمیت میدهیم، از انتخاب نخ گرفته تا طراحی هر برش و دوخت. این تعهد به کیفیت، تضمین میکند که هر ساخته دست ما، اثری ماندگار و بیهمتا باشد.</p>
|
||||||
|
<a href="shop.php" class="btn btn-primary mt-4">مشاهده مجموعه ما</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Team Section or Values -->
|
||||||
|
<section class="py-5 mt-4">
|
||||||
|
<div class="row text-center g-4">
|
||||||
|
<div class="col-md-4" data-aos="fade-up" data-aos-delay="100">
|
||||||
|
<div class="card bg-dark-2 border-secondary h-100 p-4 rounded-3">
|
||||||
|
<i class="fas fa-gem fa-3x text-primary mb-3"></i>
|
||||||
|
<h4 class="fw-bold">تعهد به کیفیت</h4>
|
||||||
|
<p class="text-muted">استفاده از بهترین مواد اولیه و کنترل کیفی دقیق در تمام مراحل تولید.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4" data-aos="fade-up" data-aos-delay="200">
|
||||||
|
<div class="card bg-dark-2 border-secondary h-100 p-4 rounded-3">
|
||||||
|
<i class="fas fa-hand-holding-heart fa-3x text-primary mb-3"></i>
|
||||||
|
<h4 class="fw-bold">هنر دست</h4>
|
||||||
|
<p class="text-muted">تمام محصولات ما با عشق و دقت توسط هنرمندان ماهر ساخته میشوند.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4" data-aos="fade-up" data-aos-delay="300">
|
||||||
|
<div class="card bg-dark-2 border-secondary h-100 p-4 rounded-3">
|
||||||
|
<i class="fas fa-leaf fa-3x text-primary mb-3"></i>
|
||||||
|
<h4 class="fw-bold">طراحی ماندگار</h4>
|
||||||
|
<p class="text-muted">خلق آثاری مدرن و در عین حال کلاسیک که هیچگاه از مد نمیافتند.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
@ -2,80 +2,78 @@
|
|||||||
session_start();
|
session_start();
|
||||||
require_once __DIR__ . '/auth_check.php';
|
require_once __DIR__ . '/auth_check.php';
|
||||||
|
|
||||||
|
// New header
|
||||||
|
require_once __DIR__ . '/header.php';
|
||||||
|
|
||||||
$flash_message = $_SESSION['flash_message'] ?? null;
|
$flash_message = $_SESSION['flash_message'] ?? null;
|
||||||
if ($flash_message) {
|
if ($flash_message) {
|
||||||
unset($_SESSION['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="row justify-content-center">
|
<div class="col-lg-8">
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h1 class="font-lalezar">افزودن محصول جدید</h1>
|
<h1 class="h2">افزودن محصول جدید</h1>
|
||||||
<a href="index.php" class="btn btn-outline-light">بازگشت</a>
|
<a href="products.php" class="btn btn-secondary">انصراف</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card bg-dark-2">
|
|
||||||
|
<div class="card shadow-sm" style="background-color: var(--admin-surface); border-color: var(--admin-border);">
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<form action="handler.php?action=add" method="post" enctype="multipart/form-data">
|
<form action="handler.php?action=add" method="post" enctype="multipart/form-data">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="name" class="form-label">نام محصول</label>
|
<label for="name" class="form-label">نام محصول</label>
|
||||||
<input type="text" class="form-control bg-dark text-white" id="name" name="name" required>
|
<input type="text" class="form-control" id="name" name="name" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="description" class="form-label">توضیحات</label>
|
<label for="description" class="form-label">توضیحات</label>
|
||||||
<textarea class="form-control bg-dark text-white" id="description" name="description" rows="3" required></textarea>
|
<textarea class="form-control" id="description" name="description" rows="4" required></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
<label for="price" class="form-label">قیمت (تومان)</label>
|
<label for="price" class="form-label">قیمت (تومان)</label>
|
||||||
<input type="number" class="form-control bg-dark text-white" id="price" name="price" required>
|
<input type="number" class="form-control" id="price" name="price" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="image" class="form-label">تصویر محصول</label>
|
<label for="image" class="form-label">تصویر محصول</label>
|
||||||
<input type="file" class="form-control bg-dark text-white" id="image" name="image" accept="image/*" required>
|
<input type="file" class="form-control" id="image" name="image" accept="image/*">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="colors" class="form-label">کدهای رنگ (اختیاری)</label>
|
<label for="colors" class="form-label">کدهای رنگ (اختیاری)</label>
|
||||||
<input type="text" class="form-control bg-dark text-white" id="colors" name="colors" placeholder="مثال: #8B4513, #2C2C2C">
|
<input type="text" class="form-control" id="colors" name="colors" placeholder="مثال: #8B4513, #2C2C2C">
|
||||||
<div class="form-text">کدهای رنگ هگزادسیمال را با کاما جدا کنید.</div>
|
<div class="form-text" style="color: var(--admin-text-secondary);">کدهای رنگ هگزادسیمال را با کاما جدا کنید.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 form-check">
|
<div class="mb-4 form-check">
|
||||||
<input type="checkbox" class="form-check-input" id="is_featured" name="is_featured" value="1">
|
<input type="checkbox" class="form-check-input" id="is_featured" name="is_featured" value="1" style="border-color: var(--admin-border);">
|
||||||
<label class="form-check-label" for="is_featured">محصول ویژه</label>
|
<label class="form-check-label" for="is_featured">این یک محصول ویژه است</label>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg">افزودن محصول</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary w-100">افزودن محصول</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<!-- Page-specific scripts -->
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
<?php if ($flash_message): ?>
|
<?php if ($flash_message): ?>
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '<?php echo $flash_message["type"] === "success" ? "عالی" : "خطا"; ?>',
|
title: '<?php echo $flash_message["type"] === "success" ? "عالی" : "خطا"; ?>',
|
||||||
html: '<?php echo addslashes($flash_message["message"]); ?>', // Use html to render <br> tags
|
html: '<?php echo addslashes($flash_message["message"]); ?>',
|
||||||
icon: '<?php echo $flash_message["type"]; ?>',
|
icon: '<?php echo $flash_message["type"]; ?>',
|
||||||
confirmButtonText: 'باشه'
|
confirmButtonText: 'باشه',
|
||||||
|
background: 'var(--admin-surface)',
|
||||||
|
color: 'var(--admin-text-primary)'
|
||||||
});
|
});
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
|
||||||
</html>
|
<?php
|
||||||
|
// New footer
|
||||||
|
require_once __DIR__ . '/footer.php';
|
||||||
|
?>
|
||||||
|
|||||||
259
admin/assets/css/admin_style.css
Normal file
259
admin/assets/css/admin_style.css
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
|
||||||
|
/* =================================================================
|
||||||
|
ADMIN PANEL MODERN STYLES
|
||||||
|
================================================================= */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--admin-bg: #1A202C; /* Very dark blue */
|
||||||
|
--admin-surface: #2D3748; /* Lighter dark blue for cards, tables */
|
||||||
|
--admin-border: #4A5568; /* Subtle borders */
|
||||||
|
--admin-accent: #FBBF24; /* Amber/Gold for highlights */
|
||||||
|
--admin-accent-hover: #F59E0B; /* Darker gold for hover */
|
||||||
|
|
||||||
|
--admin-text-primary: #EDF2F7; /* Bright, light gray for main text */
|
||||||
|
--admin-text-secondary: #A0AEC0; /* Softer gray for subtitles */
|
||||||
|
|
||||||
|
--admin-success: #38A169; /* Green */
|
||||||
|
--admin-danger: #E53E3E; /* Red */
|
||||||
|
--admin-info: #3182CE; /* Blue */
|
||||||
|
|
||||||
|
--admin-font: 'Vazirmatn', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- General Body & Typography --- */
|
||||||
|
body.admin-page {
|
||||||
|
background-color: var(--admin-bg);
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
font-family: var(--admin-font);
|
||||||
|
padding-right: 0; /* Reset previous style */
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-main-content {
|
||||||
|
padding: 2rem;
|
||||||
|
margin-right: 280px; /* Space for the new sidebar */
|
||||||
|
transition: margin-right 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--admin-accent);
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: var(--admin-accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Override Bootstrap Dark Components --- */
|
||||||
|
.table-dark {
|
||||||
|
--bs-table-bg: var(--admin-surface);
|
||||||
|
--bs-table-border-color: var(--admin-border);
|
||||||
|
--bs-table-color: var(--admin-text-primary);
|
||||||
|
--bs-table-striped-bg: #353c4a; /* Slightly lighter for striped rows */
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > :not(caption) > * > * {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
background-color: var(--admin-surface);
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
border-color: var(--admin-border);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
background-color: var(--admin-surface);
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
border-color: var(--admin-accent);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(var(--admin-accent), 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select {
|
||||||
|
background-color: var(--admin-surface);
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
border-color: var(--admin-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --- Buttons --- */
|
||||||
|
.btn-primary {
|
||||||
|
background-color: var(--admin-accent);
|
||||||
|
border-color: var(--admin-accent);
|
||||||
|
color: #1A202C; /* Dark text on gold button */
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: var(--admin-accent-hover);
|
||||||
|
border-color: var(--admin-accent-hover);
|
||||||
|
color: #1A202C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success { background-color: var(--admin-success); border-color: var(--admin-success); }
|
||||||
|
.btn-danger { background-color: var(--admin-danger); border-color: var(--admin-danger); }
|
||||||
|
.btn-info { background-color: var(--admin-info); border-color: var(--admin-info); }
|
||||||
|
|
||||||
|
|
||||||
|
/* --- New Sidebar --- */
|
||||||
|
.admin-sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 280px;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: var(--admin-surface);
|
||||||
|
border-left: 1px solid var(--admin-border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1.5rem 0;
|
||||||
|
z-index: 1100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar-header {
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 1.5rem 1.5rem 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--admin-border);
|
||||||
|
}
|
||||||
|
.admin-sidebar-header .logo {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.admin-sidebar-header .logo span {
|
||||||
|
color: var(--admin-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar .nav {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
.admin-sidebar .nav-link {
|
||||||
|
color: var(--admin-text-secondary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.9rem 1.5rem;
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
.admin-sidebar .nav-link:hover {
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
background-color: rgba(45, 55, 72, 0.5); /* #2D3748 with opacity */
|
||||||
|
}
|
||||||
|
.admin-sidebar .nav-link.active {
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
background-color: var(--admin-bg);
|
||||||
|
border-right-color: var(--admin-accent);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.admin-sidebar .nav-link .bi {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-left: 0.75rem; /* For RTL, it should be margin-left */
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar-footer {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-top: 1px solid var(--admin-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Dashboard Stat Cards --- */
|
||||||
|
.stat-card {
|
||||||
|
background-color: var(--admin-surface);
|
||||||
|
border: 1px solid var(--admin-border);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
|
||||||
|
border-color: var(--admin-accent);
|
||||||
|
}
|
||||||
|
.stat-card .icon-container {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--admin-accent);
|
||||||
|
background-color: #363e4d;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 1.5rem; /* For RTL */
|
||||||
|
}
|
||||||
|
.stat-card .stat-info h3 {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
font-weight: 800;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.stat-card .stat-info p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--admin-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Dashboard Tables & Badges --- */
|
||||||
|
.card-table .card-header {
|
||||||
|
background-color: transparent;
|
||||||
|
border-bottom: 1px solid var(--admin-border);
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.bg-processing { background-color: var(--admin-info) !important; }
|
||||||
|
.badge.bg-shipped { background-color: var(--admin-success) !important; }
|
||||||
|
.badge.bg-cancelled { background-color: var(--admin-danger) !important; }
|
||||||
|
.badge.bg-pending { background-color: #DD6B20 !important; } /* Orange */
|
||||||
|
|
||||||
|
/* --- Modal Styling --- */
|
||||||
|
.modal-content {
|
||||||
|
background-color: var(--admin-surface);
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
border: 1px solid var(--admin-border);
|
||||||
|
}
|
||||||
|
.modal-header {
|
||||||
|
border-bottom: 1px solid var(--admin-border);
|
||||||
|
}
|
||||||
|
.modal-footer {
|
||||||
|
border-top: 1px solid var(--admin-border);
|
||||||
|
}
|
||||||
|
.btn-close {
|
||||||
|
filter: invert(1) grayscale(100%) brightness(200%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.admin-main-content {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
.admin-sidebar {
|
||||||
|
transform: translateX(280px); /* For RTL */
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
.admin-sidebar.is-open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
/* Add a hamburger toggle button */
|
||||||
|
.sidebar-toggle {
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
z-index: 1200;
|
||||||
|
background: var(--admin-surface);
|
||||||
|
border: 1px solid var(--admin-border);
|
||||||
|
color: var(--admin-text-primary);
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user is logged in. If not, redirect to the login page.
|
// Check if the user is logged in. If not, redirect to the login page.
|
||||||
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
|
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
|
||||||
|
|||||||
@ -3,15 +3,12 @@ session_start();
|
|||||||
require_once __DIR__ . '/auth_check.php';
|
require_once __DIR__ . '/auth_check.php';
|
||||||
require_once __DIR__ . '/../db/config.php';
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
$flash_message = $_SESSION['flash_message'] ?? null;
|
// Sanitize and validate product ID
|
||||||
if ($flash_message) {
|
$product_id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
|
||||||
unset($_SESSION['flash_message']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$product_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
if (!$product_id) {
|
||||||
|
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'شناسه محصول نامعتبر است.'];
|
||||||
if ($product_id <= 0) {
|
header('Location: products.php');
|
||||||
header('Location: index.php');
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,88 +19,114 @@ try {
|
|||||||
$product = $stmt->fetch(PDO::FETCH_ASSOC);
|
$product = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (!$product) {
|
if (!$product) {
|
||||||
header('Location: index.php');
|
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'محصول مورد نظر یافت نشد.'];
|
||||||
|
header('Location: products.php');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
die("Error fetching product: " . $e->getMessage());
|
error_log("Database Error: " . $e->getMessage());
|
||||||
|
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'خطا در اتصال به پایگاه داده. لطفاً بعداً تلاش کنید.'];
|
||||||
|
header('Location: products.php');
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$page_title = "ویرایش محصول: " . htmlspecialchars($product['name']);
|
||||||
|
require_once 'header.php';
|
||||||
?>
|
?>
|
||||||
<!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="page-header">
|
||||||
<div class="row justify-content-center">
|
<h1 class="page-title">ویرایش محصول</h1>
|
||||||
<div class="col-md-8">
|
<nav aria-label="breadcrumb">
|
||||||
<h1 class="font-lalezar mb-4">ویرایش محصول</h1>
|
<ol class="breadcrumb">
|
||||||
<div class="card bg-dark-2">
|
<li class="breadcrumb-item"><a href="index.php">داشبورد</a></li>
|
||||||
<div class="card-body p-4">
|
<li class="breadcrumb-item"><a href="products.php">مدیریت محصولات</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page"><?php echo htmlspecialchars($product['name']); ?></li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="card-title">فرم ویرایش محصول</h5>
|
||||||
|
<a href="products.php" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<i class="fas fa-arrow-left me-2"></i>بازگشت
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
<form action="handler.php?action=edit" method="POST" enctype="multipart/form-data">
|
<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="id" value="<?php echo htmlspecialchars($product['id']); ?>">
|
||||||
<input type="hidden" name="current_image" value="<?php echo htmlspecialchars($product['image_url']); ?>">
|
<input type="hidden" name="current_image" value="<?php echo htmlspecialchars($product['image_url']); ?>">
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="row">
|
||||||
<label for="name" class="form-label">نام محصول</label>
|
<!-- Main Product Info -->
|
||||||
<input type="text" class="form-control bg-dark text-white" id="name" name="name" value="<?php echo htmlspecialchars($product['name']); ?>" required>
|
<div class="col-lg-8">
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label for="name">نام محصول</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($product['name']); ?>" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label for="description" class="form-label">توضیحات</label>
|
<div class="form-group mb-4">
|
||||||
<textarea class="form-control bg-dark text-white" id="description" name="description" rows="3" required><?php echo htmlspecialchars($product['description']); ?></textarea>
|
<label for="description">توضیحات</label>
|
||||||
|
<textarea class="form-control" id="description" name="description" rows="6" required><?php echo htmlspecialchars($product['description']); ?></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label for="price" class="form-label">قیمت (به تومان)</label>
|
<div class="row">
|
||||||
<input type="number" class="form-control bg-dark text-white" id="price" name="price" min="0" value="<?php echo htmlspecialchars($product['price']); ?>" required>
|
<div class="col-md-6">
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label for="price">قیمت (تومان)</label>
|
||||||
|
<input type="number" class="form-control" id="price" name="price" min="0" value="<?php echo htmlspecialchars($product['price']); ?>" required>
|
||||||
</div>
|
</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>
|
||||||
<div class="mb-3">
|
<div class="col-md-6">
|
||||||
<label for="image" class="form-label">تصویر محصول</label>
|
<div class="form-group mb-4">
|
||||||
<input type="file" class="form-control bg-dark text-white" id="image" name="image" accept="image/*">
|
<label for="colors">کدهای رنگ هگز</label>
|
||||||
<div class="form-text mt-2">تصویر فعلی:</div>
|
<input type="text" class="form-control" id="colors" name="colors" value="<?php echo htmlspecialchars($product['colors'] ?? ''); ?>">
|
||||||
<img src="../<?php echo htmlspecialchars($product['image_url']); ?>" alt="Current Image" class="img-thumbnail mt-2" width="100">
|
<div class="form-text">رنگها را با کاما جدا کنید (مثال: #FFFFFF, #000000).</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 form-check">
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-switch custom-switch mb-4">
|
||||||
<input type="checkbox" class="form-check-input" id="is_featured" name="is_featured" value="1" <?php echo ($product['is_featured'] ?? 0) ? 'checked' : ''; ?>>
|
<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>
|
<label class="form-check-label" for="is_featured">محصول ویژه (نمایش در صفحه اصلی)</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-end gap-2 mt-4">
|
</div>
|
||||||
<a href="index.php" class="btn btn-secondary">انصراف</a>
|
|
||||||
<button type="submit" class="btn btn-primary">بهروزرسانی محصول</button>
|
<!-- Image Upload -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label for="image">تصویر محصول</label>
|
||||||
|
<div class="image-upload-wrapper text-center">
|
||||||
|
<img src="../<?php echo htmlspecialchars($product['image_url']); ?>" alt="Current Image" class="img-thumbnail mb-3" id="image-preview" style="max-width: 180px; height: auto;">
|
||||||
|
<input type="file" class="form-control" id="image" name="image" accept="image/*" onchange="previewImage(event)">
|
||||||
|
<small class="form-text text-muted mt-2">برای تغییر، تصویر جدید را انتخاب کنید.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<a href="products.php" class="btn btn-outline-secondary">انصراف</a>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-save me-2"></i>ذخیره تغییرات
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<?php
|
||||||
|
require_once 'footer.php';
|
||||||
|
?>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
// Preview image before upload
|
||||||
<?php if ($flash_message): ?>
|
function previewImage(event) {
|
||||||
Swal.fire({
|
const reader = new FileReader();
|
||||||
title: '<?php echo $flash_message["type"] === "success" ? "عالی" : "خطا"; ?>',
|
reader.onload = function(){
|
||||||
html: '<?php echo addslashes($flash_message["message"]); ?>', // Use html to render <br> tags
|
const output = document.getElementById('image-preview');
|
||||||
icon: '<?php echo $flash_message["type"]; ?>',
|
output.src = reader.result;
|
||||||
confirmButtonText: 'باشه'
|
};
|
||||||
});
|
if (event.target.files[0]) {
|
||||||
<?php endif; ?>
|
reader.readAsDataURL(event.target.files[0]);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
7
admin/footer.php
Normal file
7
admin/footer.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
</div><!-- /.container-fluid -->
|
||||||
|
</main><!-- /.admin-main-content -->
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,10 +1,9 @@
|
|||||||
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
require_once __DIR__ . '/../db/config.php';
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
require_once __DIR__ . '/auth_check.php';
|
require_once __DIR__ . '/auth_check.php';
|
||||||
|
|
||||||
$action = $_REQUEST['action'] ?? '';
|
$action = $_REQUEST['action'] ?? '';
|
||||||
|
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
|
|
||||||
// Default redirect location
|
// Default redirect location
|
||||||
@ -30,6 +29,7 @@ switch ($action) {
|
|||||||
$image_path = '';
|
$image_path = '';
|
||||||
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
|
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
|
||||||
$upload_dir = __DIR__ . '/../assets/images/products/';
|
$upload_dir = __DIR__ . '/../assets/images/products/';
|
||||||
|
|
||||||
if (!is_dir($upload_dir)) {
|
if (!is_dir($upload_dir)) {
|
||||||
if (!mkdir($upload_dir, 0777, true)) {
|
if (!mkdir($upload_dir, 0777, true)) {
|
||||||
$errors[] = "Image directory does not exist and could not be created.";
|
$errors[] = "Image directory does not exist and could not be created.";
|
||||||
@ -41,7 +41,6 @@ switch ($action) {
|
|||||||
} else {
|
} else {
|
||||||
$filename = uniqid('product_', true) . '_' . basename($_FILES['image']['name']);
|
$filename = uniqid('product_', true) . '_' . basename($_FILES['image']['name']);
|
||||||
$target_file = $upload_dir . $filename;
|
$target_file = $upload_dir . $filename;
|
||||||
|
|
||||||
if (move_uploaded_file($_FILES['image']['tmp_name'], $target_file)) {
|
if (move_uploaded_file($_FILES['image']['tmp_name'], $target_file)) {
|
||||||
$image_path = 'assets/images/products/' . $filename;
|
$image_path = 'assets/images/products/' . $filename;
|
||||||
} else {
|
} else {
|
||||||
@ -55,39 +54,31 @@ switch ($action) {
|
|||||||
UPLOAD_ERR_FORM_SIZE => "The uploaded file exceeds the maximum size specified in the form.",
|
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_PARTIAL => "The file was only partially uploaded.",
|
||||||
UPLOAD_ERR_NO_FILE => "No file was selected for upload.",
|
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_NO_TMP_DIR => "Server configuration is missing a temporary folder for uploads.",
|
||||||
UPLOAD_ERR_CANT_WRITE => "Server error: Failed to write the uploaded file to disk.",
|
UPLOAD_ERR_CANT_WRITE => "Failed to write file to disk. Check permissions.",
|
||||||
UPLOAD_ERR_EXTENSION => "A PHP extension prevented the file upload.",
|
UPLOAD_ERR_EXTENSION => "A PHP extension stopped the file upload.",
|
||||||
];
|
];
|
||||||
$error_message = $upload_errors[$file_error] ?? "An unknown upload error occurred (Code: {$file_error}).";
|
if ($file_error !== UPLOAD_ERR_NO_FILE) {
|
||||||
// Only trigger error if the action is 'add', where image is mandatory
|
$errors[] = $upload_errors[$file_error] ?? "An unknown error occurred during file upload.";
|
||||||
if ($action === 'add') {
|
|
||||||
$errors[] = "Image Upload Failed: " . $error_message;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($errors)) {
|
if (empty($errors)) {
|
||||||
try {
|
|
||||||
$sql = "INSERT INTO products (name, description, price, image_url, colors, is_featured) VALUES (?, ?, ?, ?, ?, ?)";
|
$sql = "INSERT INTO products (name, description, price, image_url, colors, is_featured) VALUES (?, ?, ?, ?, ?, ?)";
|
||||||
$stmt = $pdo->prepare($sql);
|
$stmt = $pdo->prepare($sql);
|
||||||
$stmt->execute([$name, $description, $price, $image_path, $colors, $is_featured]);
|
$stmt->execute([$name, $description, $price, $image_path, $colors, $is_featured]);
|
||||||
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'محصول با موفقیت اضافه شد!'];
|
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'محصول با موفقیت اضافه شد!'];
|
||||||
$redirect_to = 'index.php';
|
$redirect_to = 'products.php';
|
||||||
} catch (PDOException $e) {
|
|
||||||
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'خطا در افزودن محصول: ' . $e->getMessage()];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$error_message = 'لطفاً تمام خطاها را برطرف کنید:<br><br>' . implode('<br>', $errors);
|
$_SESSION['flash_message'] = ['type' => 'error', 'message' => implode("<br>", $errors)];
|
||||||
$_SESSION['flash_message'] = ['type' => 'error', 'message' => $error_message];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'edit':
|
case 'edit':
|
||||||
$id = $_POST['id'] ?? $_GET['id'] ?? null;
|
$redirect_to = 'products.php'; // Default redirect on success or if ID is missing
|
||||||
$redirect_to = 'edit_product.php?id=' . $id;
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$id = filter_var($id, FILTER_VALIDATE_INT);
|
$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
|
||||||
$name = trim($_POST['name'] ?? '');
|
$name = trim($_POST['name'] ?? '');
|
||||||
$description = trim($_POST['description'] ?? '');
|
$description = trim($_POST['description'] ?? '');
|
||||||
$price = filter_var($_POST['price'], FILTER_VALIDATE_FLOAT);
|
$price = filter_var($_POST['price'], FILTER_VALIDATE_FLOAT);
|
||||||
@ -97,73 +88,82 @@ switch ($action) {
|
|||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
if (!$id) {
|
if (!$id) {
|
||||||
$errors[] = "شناسه محصول نامعتبر است.";
|
$errors[] = "Invalid product ID.";
|
||||||
|
} else {
|
||||||
|
$redirect_to = "edit_product.php?id=$id"; // Redirect back to the edit form on error
|
||||||
}
|
}
|
||||||
// Other validations...
|
|
||||||
|
|
||||||
$image_path = $_POST['current_image'] ?? '';
|
if (empty($name)) $errors[] = "Product name is required.";
|
||||||
|
if (empty($description)) $errors[] = "Description is required.";
|
||||||
|
if ($price === false) $errors[] = "Price is invalid or missing.";
|
||||||
|
|
||||||
|
|
||||||
|
$current_image_path = $_POST['current_image'] ?? '';
|
||||||
|
$image_path = $current_image_path;
|
||||||
|
|
||||||
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
|
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
|
||||||
$upload_dir = __DIR__ . '/../assets/images/products/';
|
$upload_dir = __DIR__ . '/../assets/images/products/';
|
||||||
|
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
|
||||||
|
|
||||||
|
if (is_writable($upload_dir)) {
|
||||||
$filename = uniqid('product_', true) . '_' . basename($_FILES['image']['name']);
|
$filename = uniqid('product_', true) . '_' . basename($_FILES['image']['name']);
|
||||||
$target_file = $upload_dir . $filename;
|
$target_file = $upload_dir . $filename;
|
||||||
if (move_uploaded_file($_FILES['image']['tmp_name'], $target_file)) {
|
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;
|
$image_path = 'assets/images/products/' . $filename;
|
||||||
|
// Optionally, delete the old image if it's different
|
||||||
|
if ($current_image_path && file_exists(__DIR__ . '/../' . $current_image_path)) {
|
||||||
|
// unlink(__DIR__ . '/../' . $current_image_path);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$errors[] = "خطا در آپلود تصویر جدید.";
|
$errors[] = "Failed to move uploaded file.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$errors[] = "Image directory is not writable.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($errors)) {
|
if (empty($errors)) {
|
||||||
try {
|
|
||||||
$sql = "UPDATE products SET name = ?, description = ?, price = ?, image_url = ?, colors = ?, is_featured = ? WHERE id = ?";
|
$sql = "UPDATE products SET name = ?, description = ?, price = ?, image_url = ?, colors = ?, is_featured = ? WHERE id = ?";
|
||||||
$stmt = $pdo->prepare($sql);
|
$stmt = $pdo->prepare($sql);
|
||||||
$stmt->execute([$name, $description, $price, $image_path, $colors, $is_featured, $id]);
|
$stmt->execute([$name, $description, $price, $image_path, $colors, $is_featured, $id]);
|
||||||
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'محصول با موفقیت ویرایش شد!'];
|
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'محصول با موفقیت بهروزرسانی شد!'];
|
||||||
$redirect_to = 'index.php';
|
$redirect_to = 'products.php';
|
||||||
} catch (PDOException $e) {
|
|
||||||
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'خطا در ویرایش محصول: ' . $e->getMessage()];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$error_message = 'فرم دارای خطا است:<br><br>' . implode('<br>', $errors);
|
$_SESSION['flash_message'] = ['type' => 'error', 'message' => implode("<br>", $errors)];
|
||||||
$_SESSION['flash_message'] = ['type' => 'error', 'message' => $error_message];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$id = filter_var($_GET['id'], FILTER_VALIDATE_INT);
|
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
|
||||||
if ($id) {
|
if ($id) {
|
||||||
try {
|
|
||||||
// First, get the image path to delete the file
|
// First, get the image path to delete the file
|
||||||
$stmt = $pdo->prepare("SELECT image_url FROM products WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT image_url FROM products WHERE id = ?");
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$image_to_delete = $stmt->fetchColumn();
|
$product = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
// Delete the record
|
if ($product && !empty($product['image_url'])) {
|
||||||
|
$image_file = __DIR__ . '/../' . $product['image_url'];
|
||||||
|
if (file_exists($image_file)) {
|
||||||
|
// unlink($image_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, delete the record from the database
|
||||||
$sql = "DELETE FROM products WHERE id = ?";
|
$sql = "DELETE FROM products WHERE id = ?";
|
||||||
$stmt = $pdo->prepare($sql);
|
$stmt = $pdo->prepare($sql);
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
|
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'محصول با موفقیت حذف شد!'];
|
||||||
// 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 {
|
} else {
|
||||||
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'شناسه محصول نامعتبر است.'];
|
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'شناسه محصول برای حذف نامعتبر است.'];
|
||||||
}
|
}
|
||||||
$redirect_to = 'index.php';
|
$redirect_to = 'products.php';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'عملیات نامعتبر است.'];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect back after the action
|
header("Location: " . $redirect_to);
|
||||||
header('Location: ' . $redirect_to);
|
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
27
admin/header.php
Normal file
27
admin/header.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<!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">
|
||||||
|
|
||||||
|
<!-- 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@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Styles -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/admin_style.css?v=<?php echo time(); ?>">
|
||||||
|
|
||||||
|
<!-- SweetAlert2 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
</head>
|
||||||
|
<body class="admin-page">
|
||||||
|
|
||||||
|
<?php require_once 'nav.php'; ?>
|
||||||
|
|
||||||
|
<main class="admin-main-content">
|
||||||
|
<div class="container-fluid">
|
||||||
161
admin/index.php
161
admin/index.php
@ -3,12 +3,24 @@ session_start();
|
|||||||
require_once __DIR__ . '/auth_check.php';
|
require_once __DIR__ . '/auth_check.php';
|
||||||
require_once __DIR__ . '/../db/config.php';
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
|
// New header - includes nav, head, and opening body/main tags
|
||||||
|
require_once __DIR__ . '/header.php';
|
||||||
|
|
||||||
|
$dashboard_error = null;
|
||||||
|
$total_products = 0;
|
||||||
|
$total_orders = 0;
|
||||||
|
$recent_orders = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
$stmt = $pdo->query("SELECT id, name, price FROM products ORDER BY created_at DESC");
|
|
||||||
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$total_products = $pdo->query("SELECT COUNT(*) FROM products")->fetchColumn();
|
||||||
|
$total_orders = $pdo->query("SELECT COUNT(*) FROM orders")->fetchColumn();
|
||||||
|
$recent_orders = $pdo->query("SELECT id, customer_name, total_amount, `status`, created_at FROM orders ORDER BY created_at DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
die("Error fetching products: " . $e->getMessage());
|
$dashboard_error = "<strong>خطا در بارگذاری اطلاعات داشبورد:</strong> " . $e->getMessage();
|
||||||
|
$dashboard_error .= "<br><br>این خطا معمولاً به دلیل قدیمی بودن ساختار دیتابیس رخ میدهد. لطفاً برای بهروزرسانی به <a href='../migrate.php' class='alert-link'>صفحه مایگریشن</a> بروید.";
|
||||||
}
|
}
|
||||||
|
|
||||||
$flash_message = $_SESSION['flash_message'] ?? null;
|
$flash_message = $_SESSION['flash_message'] ?? null;
|
||||||
@ -16,54 +28,86 @@ if ($flash_message) {
|
|||||||
unset($_SESSION['flash_message']);
|
unset($_SESSION['flash_message']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to map status to a badge class
|
||||||
|
function get_status_badge($status) {
|
||||||
|
switch (strtolower($status)) {
|
||||||
|
case 'processing':
|
||||||
|
return 'bg-processing';
|
||||||
|
case 'shipped':
|
||||||
|
return 'bg-shipped';
|
||||||
|
case 'cancelled':
|
||||||
|
return 'bg-cancelled';
|
||||||
|
default:
|
||||||
|
return 'bg-pending';
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<!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">
|
<h1 class="h2 mb-4">داشبورد</h1>
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
||||||
<h1 class="font-lalezar">مدیریت محصولات</h1>
|
<?php if ($dashboard_error): ?>
|
||||||
<div class="d-flex gap-2">
|
<div class="alert alert-danger"><?php echo $dashboard_error; ?></div>
|
||||||
<a href="add_product.php" class="btn btn-success">+ افزودن محصول جدید</a>
|
<?php else: ?>
|
||||||
<a href="logout.php" class="btn btn-outline-danger">خروج</a>
|
|
||||||
|
<!-- Stat Cards -->
|
||||||
|
<div class="row g-4 mb-4">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="icon-container">
|
||||||
|
<i class="bi bi-box-seam"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<p>کل محصولات</p>
|
||||||
|
<h3><?php echo htmlspecialchars($total_products); ?></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="icon-container">
|
||||||
|
<i class="bi bi-receipt"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<p>کل سفارشات</p>
|
||||||
|
<h3><?php echo htmlspecialchars($total_orders); ?></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Recent Orders -->
|
||||||
|
<div class="card shadow-sm card-table" style="background-color: var(--admin-surface); border-color: var(--admin-border);">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0">آخرین سفارشات</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-dark table-striped table-hover">
|
<table class="table table-dark table-hover align-middle">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">#</th>
|
<th>شماره سفارش</th>
|
||||||
<th scope="col">نام محصول</th>
|
<th>نام مشتری</th>
|
||||||
<th scope="col">قیمت</th>
|
<th>مبلغ کل</th>
|
||||||
<th scope="col">عملیات</th>
|
<th>وضعیت</th>
|
||||||
|
<th>تاریخ</th>
|
||||||
|
<th class="text-end"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php if (empty($products)): ?>
|
<?php if (empty($recent_orders)): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-center">هیچ محصولی یافت نشد.</td>
|
<td colspan="6" class="text-center py-4">هیچ سفارشی یافت نشد.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($products as $product): ?>
|
<?php foreach ($recent_orders as $order): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row"><?php echo htmlspecialchars($product['id']); ?></th>
|
<td>#<?php echo htmlspecialchars($order['id']); ?></td>
|
||||||
<td><?php echo htmlspecialchars($product['name']); ?></td>
|
<td><?php echo htmlspecialchars($order['customer_name']); ?></td>
|
||||||
<td><?php echo number_format($product['price']); ?> تومان</td>
|
<td><?php echo number_format($order['total_amount']); ?> تومان</td>
|
||||||
<td>
|
<td><span class="badge <?php echo get_status_badge($order['status']); ?>"><?php echo htmlspecialchars($order['status']); ?></span></td>
|
||||||
<a href="edit_product.php?id=<?php echo $product['id']; ?>" class="btn btn-sm btn-primary">ویرایش</a>
|
<td><?php echo date('Y-m-d', strtotime($order['created_at'])); ?></td>
|
||||||
<a href="handler.php?action=delete&id=<?php echo $product['id']; ?>" class="btn btn-sm btn-danger delete-btn">حذف</a>
|
<td class="text-end">
|
||||||
|
<a href="orders.php?id=<?php echo $order['id']; ?>" class="btn btn-sm btn-outline-info">مشاهده</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
@ -71,47 +115,28 @@ if ($flash_message) {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
|
||||||
<a href="../index.php" class="btn btn-outline-light">بازگشت به سایت</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<!-- Page-specific scripts -->
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
// Flash message handling
|
// Flash message handling (if any)
|
||||||
<?php if ($flash_message): ?>
|
<?php if ($flash_message): ?>
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '<?php echo $flash_message["type"] === "success" ? "عالی" : "خطا"; ?>',
|
title: '<?php echo $flash_message["type"] === "success" ? "موفق" : "خطا"; ?>',
|
||||||
html: '<?php echo addslashes($flash_message["message"]); ?>',
|
html: '<?php echo addslashes($flash_message["message"]); ?>',
|
||||||
icon: '<?php echo $flash_message["type"]; ?>',
|
icon: '<?php echo $flash_message["type"]; ?>',
|
||||||
confirmButtonText: 'باشه'
|
confirmButtonText: 'باشه',
|
||||||
|
background: 'var(--admin-surface)',
|
||||||
|
color: 'var(--admin-text-primary)'
|
||||||
});
|
});
|
||||||
<?php endif; ?>
|
<?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>
|
</script>
|
||||||
</body>
|
|
||||||
</html>
|
<?php
|
||||||
|
// New footer
|
||||||
|
require_once __DIR__ . '/footer.php';
|
||||||
|
?>
|
||||||
|
|||||||
@ -32,6 +32,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
<meta name="robots" content="noindex, nofollow">
|
<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://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(); ?>">
|
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-dark text-white">
|
<body class="bg-dark text-white">
|
||||||
|
|
||||||
@ -43,7 +44,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
<h1 class="font-lalezar text-center mb-4">ورود به پنل</h1>
|
<h1 class="font-lalezar text-center mb-4">ورود به پنل</h1>
|
||||||
<p class="text-center text-muted mb-4">رمز عبور: admin123</p>
|
<p class="text-center text-muted mb-4">رمز عبور: admin123</p>
|
||||||
<?php if ($error): ?>
|
<?php if ($error): ?>
|
||||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
Swal.fire({
|
||||||
|
toast: true,
|
||||||
|
position: 'top-start',
|
||||||
|
icon: 'error',
|
||||||
|
title: '<?php echo $error; ?>',
|
||||||
|
showConfirmButton: false,
|
||||||
|
timer: 3000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
background: '#2C2C2C',
|
||||||
|
color: '#D5D5D5'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
|||||||
42
admin/nav.php
Normal file
42
admin/nav.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php $current_page = basename($_SERVER['PHP_SELF']); ?>
|
||||||
|
<div class="admin-sidebar">
|
||||||
|
<div class="admin-sidebar-header">
|
||||||
|
<a href="index.php" class="logo">آتیمه<span>.</span></a>
|
||||||
|
</div>
|
||||||
|
<ul class="nav flex-column">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?php echo ($current_page == 'index.php') ? 'active' : ''; ?>" href="index.php">
|
||||||
|
<i class="bi bi-grid-1x2-fill"></i>
|
||||||
|
<span>داشبورد</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?php echo ($current_page == 'products.php' || $current_page == 'add_product.php' || $current_page == 'edit_product.php') ? 'active' : ''; ?>" href="products.php">
|
||||||
|
<i class="bi bi-box-seam"></i>
|
||||||
|
<span>محصولات</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?php echo ($current_page == 'orders.php') ? 'active' : ''; ?>" href="orders.php">
|
||||||
|
<i class="bi bi-card-checklist"></i>
|
||||||
|
<span>سفارشات</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="admin-sidebar-footer">
|
||||||
|
<a href="../index.php" target="_blank" class="btn btn-outline-secondary w-100 mb-2">
|
||||||
|
<i class="bi bi-box-arrow-up-right"></i> مشاهده سایت
|
||||||
|
</a>
|
||||||
|
<a href="logout.php" class="btn btn-outline-danger w-100">
|
||||||
|
<i class="bi bi-box-arrow-right"></i> خروج
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn sidebar-toggle d-lg-none" type="button" onclick="toggleSidebar()">
|
||||||
|
<i class="bi bi-list"></i>
|
||||||
|
</button>
|
||||||
|
<script>
|
||||||
|
function toggleSidebar() {
|
||||||
|
document.querySelector('.admin-sidebar').classList.toggle('is-open');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
131
admin/orders.php
Normal file
131
admin/orders.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/auth_check.php';
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
|
// New header
|
||||||
|
require_once __DIR__ . '/header.php';
|
||||||
|
|
||||||
|
// Fetch orders from the database
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->query("SELECT * FROM orders ORDER BY created_at DESC");
|
||||||
|
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$error_message = "خطا در دریافت اطلاعات سفارشات: " . $e->getMessage();
|
||||||
|
$orders = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to map status to a badge class
|
||||||
|
function get_status_badge($status) {
|
||||||
|
switch (strtolower($status)) {
|
||||||
|
case 'processing':
|
||||||
|
return 'bg-processing';
|
||||||
|
case 'shipped':
|
||||||
|
return 'bg-shipped';
|
||||||
|
case 'cancelled':
|
||||||
|
return 'bg-cancelled';
|
||||||
|
default:
|
||||||
|
return 'bg-pending';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1 class="h2">مدیریت سفارشات</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (isset($error_message)): ?>
|
||||||
|
<div class="alert alert-danger"><?php echo $error_message; ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="card shadow-sm card-table" style="background-color: var(--admin-surface); border-color: var(--admin-border);">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-dark table-hover align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>شماره</th>
|
||||||
|
<th>نام مشتری</th>
|
||||||
|
<th>مبلغ کل</th>
|
||||||
|
<th>وضعیت</th>
|
||||||
|
<th>تاریخ</th>
|
||||||
|
<th class="text-end">عملیات</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($orders)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center py-4">هیچ سفارشی یافت نشد.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($orders as $order): ?>
|
||||||
|
<tr>
|
||||||
|
<td>#<?php echo $order['id']; ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($order['customer_name']); ?></td>
|
||||||
|
<td><?php echo number_format($order['total_amount']); ?> تومان</td>
|
||||||
|
<td><span class="badge <?php echo get_status_badge($order['status']); ?>"><?php echo htmlspecialchars($order['status']); ?></span></td>
|
||||||
|
<td><?php echo date("Y-m-d", strtotime($order['created_at'])); ?></td>
|
||||||
|
<td class="text-end">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-info" data-bs-toggle="modal" data-bs-target="#orderModal<?php echo $order['id']; ?>">
|
||||||
|
<i class="bi bi-eye-fill"></i> مشاهده
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- Modal for order details -->
|
||||||
|
<div class="modal fade" id="orderModal<?php echo $order['id']; ?>" tabindex="-1" aria-labelledby="orderModalLabel<?php echo $order['id']; ?>" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="orderModalLabel<?php echo $order['id']; ?>">جزئیات سفارش #<?php echo $order['id']; ?></h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p><strong>نام:</strong> <?php echo htmlspecialchars($order['customer_name']); ?></p>
|
||||||
|
<p><strong>ایمیل:</strong> <?php echo htmlspecialchars($order['customer_email']); ?></p>
|
||||||
|
<p><strong>تلفن:</strong> <?php echo htmlspecialchars($order['customer_phone'] ?? '-'); ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p><strong>آدرس:</strong> <?php echo htmlspecialchars($order['customer_address']); ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<h6>محصولات خریداری شده:</h6>
|
||||||
|
<?php
|
||||||
|
$items = json_decode($order['items_json'], true);
|
||||||
|
if ($items):
|
||||||
|
?>
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<?php foreach($items as $item): ?>
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center" style="background: none; color: var(--admin-text-primary);">
|
||||||
|
<?php echo htmlspecialchars($item['name']); ?>
|
||||||
|
<span class="badge bg-secondary rounded-pill">
|
||||||
|
<?php echo $item['quantity']; ?> عدد - <?php echo number_format($item['price']); ?> ت
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>جزئیات محصولات موجود نیست.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<p class="w-100 text-start"><strong>مبلغ نهایی: <?php echo number_format($order['total_amount']); ?> تومان</strong></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// New footer
|
||||||
|
require_once __DIR__ . '/footer.php';
|
||||||
|
?>
|
||||||
119
admin/products.php
Normal file
119
admin/products.php
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/auth_check.php';
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
|
// New header - includes nav, head, and opening body/main tags
|
||||||
|
require_once __DIR__ . '/header.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) {
|
||||||
|
// In a real app, log this error instead of just dying
|
||||||
|
die("Error fetching products: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$flash_message = $_SESSION['flash_message'] ?? null;
|
||||||
|
if ($flash_message) {
|
||||||
|
unset($_SESSION['flash_message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!-- Page Title and Action Button -->
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1 class="h2">مدیریت محصولات</h1>
|
||||||
|
<a href="add_product.php" class="btn btn-primary">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i> افزودن محصول
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Products Table -->
|
||||||
|
<div class="card shadow-sm" style="background-color: var(--admin-surface); border-color: var(--admin-border);">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-dark table-striped table-hover align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">#</th>
|
||||||
|
<th scope="col">نام محصول</th>
|
||||||
|
<th scope="col">قیمت</th>
|
||||||
|
<th scope="col" class="text-end">عملیات</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($products)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="text-center py-4">هیچ محصولی یافت نشد.</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 class="text-end">
|
||||||
|
<a href="edit_product.php?id=<?php echo $product['id']; ?>" class="btn btn-sm btn-info">
|
||||||
|
<i class="bi bi-pencil-fill"></i> ویرایش
|
||||||
|
</a>
|
||||||
|
<a href="handler.php?action=delete&id=<?php echo $product['id']; ?>" class="btn btn-sm btn-danger delete-btn">
|
||||||
|
<i class="bi bi-trash-fill"></i> حذف
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Page-specific scripts -->
|
||||||
|
<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: 'باشه',
|
||||||
|
background: 'var(--admin-surface)',
|
||||||
|
color: 'var(--admin-text-primary)'
|
||||||
|
});
|
||||||
|
<?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: 'var(--admin-danger)',
|
||||||
|
cancelButtonColor: 'var(--admin-info)',
|
||||||
|
confirmButtonText: 'بله، حذف کن!',
|
||||||
|
cancelButtonText: 'انصراف',
|
||||||
|
background: 'var(--admin-surface)',
|
||||||
|
color: 'var(--admin-text-primary)'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
window.location.href = href;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// New footer - includes closing tags
|
||||||
|
require_once __DIR__ . '/footer.php';
|
||||||
|
?>
|
||||||
1
admin/test.php
Normal file
1
admin/test.php
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?php phpinfo(); ?>
|
||||||
248
assets/css/auth_style.css
Normal file
248
assets/css/auth_style.css
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
Auth Pages - Dark Theme Overhaul
|
||||||
|
Using global variables from custom.css
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--body-font);
|
||||||
|
/* The background is set by the body rule in custom.css */
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-wrapper {
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-bg {
|
||||||
|
flex: 1;
|
||||||
|
background: url('https://images.pexels.com/photos/193902/pexels-photo-193902.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') no-repeat center center;
|
||||||
|
background-size: cover;
|
||||||
|
position: relative;
|
||||||
|
display: none; /* Hide on small screens */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.auth-bg {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-bg::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-bg-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
max-width: 450px;
|
||||||
|
color: var(--white-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-bg-content h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-shadow: 1px 1px 8px rgba(0,0,0,0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-bg-content p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form-wrapper {
|
||||||
|
flex-grow: 1; /* Take up all space on mobile */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.auth-form-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form-container {
|
||||||
|
max-width: 420px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form-container .form-header h2 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--heading-color);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form-container .form-header p {
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form-wrapper .form-control {
|
||||||
|
height: 52px;
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 20px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form-wrapper .form-control:focus {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(192, 160, 128, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form-wrapper .btn-primary {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: var(--heading-color);
|
||||||
|
height: 50px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form-wrapper .btn-primary:hover {
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
border-color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-footer a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
|
||||||
|
background-color: rgba(220, 53, 69, 0.15);
|
||||||
|
|
||||||
|
border-color: rgba(220, 53, 69, 0.5);
|
||||||
|
|
||||||
|
color: #f8d7da;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
color: var(--text-color);
|
||||||
|
|
||||||
|
opacity: 0.6;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.separator::before, .separator::after {
|
||||||
|
|
||||||
|
content: '';
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.separator:not(:empty)::before {
|
||||||
|
|
||||||
|
margin-right: .5em;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.separator:not(:empty)::after {
|
||||||
|
|
||||||
|
margin-left: .5em;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.btn-google {
|
||||||
|
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
|
||||||
|
color: var(--text-color);
|
||||||
|
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.btn-google:hover {
|
||||||
|
|
||||||
|
background-color: var(--background-color);
|
||||||
|
|
||||||
|
border-color: var(--text-color);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.btn-google:disabled {
|
||||||
|
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,38 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
:root variables for the default Light Theme
|
Default Dark Theme Variables.
|
||||||
|
Light theme has been removed.
|
||||||
*/
|
*/
|
||||||
:root {
|
: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 */
|
--primary-color: #C0A080; /* Lighter tan for accents in dark mode */
|
||||||
--secondary-color: #8B4513; /* Darker brown */
|
--secondary-color: #8B4513; /* Darker brown */
|
||||||
--accent-color: #D2B48C;
|
--accent-color: #D2B48C;
|
||||||
--background-color: #1A1A1A; /* Very dark grey, almost black */
|
--background-color: #1A1A1A; /* Very dark grey, almost black */
|
||||||
--surface-color: #2C2C2C; /* Dark grey for cards and surfaces */
|
--surface-color: #2C2C2C; /* Dark grey for cards and surfaces */
|
||||||
--text-color: #D5D5D5; /* Light grey for body text */
|
--text-color: #EAEAEA; /* Brighter grey for better readability */
|
||||||
--heading-color: #FFFFFF; /* White for headings */
|
--heading-color: #FFFFFF; /* White for headings */
|
||||||
--border-color: #444444; /* Grey for borders */
|
--border-color: #444444; /* Grey for borders */
|
||||||
--footer-bg: #111111; /* Even darker for footer */
|
--footer-bg: #111111; /* Even darker for footer */
|
||||||
}
|
--white-color: #FFFFFF;
|
||||||
|
|
||||||
|
--font-family-sans-serif: 'Vazirmatn', sans-serif;
|
||||||
|
--body-font: 'Vazirmatn', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: var(--body-font);
|
font-family: var(--body-font);
|
||||||
@ -64,16 +48,12 @@ a:hover {
|
|||||||
.btn-primary {
|
.btn-primary {
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
color: var(--background-color); /* To have contrast in both themes */
|
color: var(--heading-color);
|
||||||
padding: 12px 30px;
|
padding: 12px 30px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
html.dark .btn-primary {
|
|
||||||
color: var(--heading-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.btn-primary:hover, .btn-primary:focus {
|
.btn-primary:hover, .btn-primary:focus {
|
||||||
background-color: var(--secondary-color);
|
background-color: var(--secondary-color);
|
||||||
@ -91,13 +71,13 @@ html.dark .btn-primary {
|
|||||||
}
|
}
|
||||||
.btn-outline-primary:hover {
|
.btn-outline-primary:hover {
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
color: var(--white-color);
|
color: var(--heading-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Header --- */
|
/* --- Header --- */
|
||||||
.site-header {
|
.site-header {
|
||||||
background-color: var(--surface-color);
|
background-color: var(--surface-color);
|
||||||
box-shadow: 0 2px 15px rgba(0,0,0,0.05);
|
box-shadow: 0 2px 15px rgba(0,0,0,0.2);
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
@ -105,10 +85,6 @@ html.dark .btn-primary {
|
|||||||
z-index: 1020;
|
z-index: 1020;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.dark .site-header {
|
|
||||||
box-shadow: 0 2px 15px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-header .nav-link,
|
.site-header .nav-link,
|
||||||
.site-header a {
|
.site-header a {
|
||||||
color: var(--text-color) !important;
|
color: var(--text-color) !important;
|
||||||
@ -125,14 +101,9 @@ html.dark .site-header {
|
|||||||
|
|
||||||
.site-header .badge {
|
.site-header .badge {
|
||||||
background-color: var(--primary-color) !important;
|
background-color: var(--primary-color) !important;
|
||||||
color: var(--background-color) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark .site-header .badge {
|
|
||||||
color: var(--heading-color) !important;
|
color: var(--heading-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- Footer --- */
|
/* --- Footer --- */
|
||||||
.site-footer {
|
.site-footer {
|
||||||
background-color: var(--footer-bg);
|
background-color: var(--footer-bg);
|
||||||
@ -158,23 +129,14 @@ html.dark .site-header .badge {
|
|||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
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);
|
box-shadow: 0 5px 20px rgba(0,0,0,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card:hover {
|
.product-card:hover {
|
||||||
transform: translateY(-8px);
|
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);
|
box-shadow: 0 15px 30px rgba(0,0,0,0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.product-card .product-image img {
|
.product-card .product-image img {
|
||||||
transition: transform 0.5s ease;
|
transition: transform 0.5s ease;
|
||||||
}
|
}
|
||||||
@ -217,13 +179,7 @@ html.dark .product-card:hover {
|
|||||||
box-shadow: 0 0 0 3px var(--surface-color), 0 0 0 5px var(--primary-color);
|
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*="#000000"] + label {
|
||||||
.color-swatches [style*="#FFFFFF"] + label,
|
|
||||||
.color-swatches [style*="#ffffff"] + label {
|
|
||||||
border-color: #dedede;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark .color-swatches [style*="#000000"] + label {
|
|
||||||
border-color: #555;
|
border-color: #555;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +220,6 @@ html.dark .color-swatches [style*="#000000"] + label {
|
|||||||
/* --- Responsive Design --- */
|
/* --- Responsive Design --- */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|
||||||
|
|
||||||
/* About Us Mobile */
|
/* About Us Mobile */
|
||||||
#about-us .display-5 {
|
#about-us .display-5 {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
@ -291,26 +246,31 @@ html.dark .color-swatches [style*="#000000"] + label {
|
|||||||
.site-footer .col-lg-2, .site-footer .col-lg-3, .site-footer .col-lg-4 {
|
.site-footer .col-lg-2, .site-footer .col-lg-3, .site-footer .col-lg-4 {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Center flex items in the footer on mobile */
|
||||||
|
.site-footer .d-flex {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Header Icon Fixes for Dark Mode --- */
|
/* --- Header Icon Fixes for Dark Mode --- */
|
||||||
|
|
||||||
/* Ensure theme toggle and other header buttons are visible in dark mode */
|
/* Ensure theme toggle and other header buttons are visible in dark mode */
|
||||||
html.dark .site-header .btn {
|
.site-header .btn {
|
||||||
color: var(--text-color); /* Use the light text color from dark theme variables */
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
html.dark .site-header .btn:hover {
|
.site-header .btn:hover {
|
||||||
color: var(--primary-color); /* Use a hover color */
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix for bootstrap's default hamburger icon in dark mode */
|
/* Fix for bootstrap's default hamburger icon in dark mode */
|
||||||
html.dark .navbar-toggler-icon {
|
.navbar-toggler-icon {
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix for the border color of the toggler button itself */
|
/* Fix for the border color of the toggler button itself */
|
||||||
html.dark .navbar-toggler {
|
.navbar-toggler {
|
||||||
border-color: rgba(255, 255, 255, 0.15);
|
border-color: rgba(255, 255, 255, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,8 +282,8 @@ html.dark .navbar-toggler {
|
|||||||
|
|
||||||
/* --- Custom Gold Button --- */
|
/* --- Custom Gold Button --- */
|
||||||
.btn-outline-gold {
|
.btn-outline-gold {
|
||||||
border-color: #D4AF37; /* Gold */
|
border-color: var(--accent-color);
|
||||||
color: #D4AF37;
|
color: var(--accent-color);
|
||||||
padding: 12px 30px;
|
padding: 12px 30px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
@ -331,20 +291,11 @@ html.dark .navbar-toggler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline-gold:hover {
|
.btn-outline-gold:hover {
|
||||||
background-color: #D4AF37;
|
|
||||||
color: var(--white-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark .btn-outline-gold {
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark .btn-outline-gold:hover {
|
|
||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
color: var(--heading-color);
|
color: var(--heading-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- Hero Section Text --- */
|
/* --- Hero Section Text --- */
|
||||||
.hero-section {
|
.hero-section {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -368,10 +319,514 @@ html.dark .btn-outline-gold:hover {
|
|||||||
text-shadow: 0 2px 8px rgba(0,0,0,0.5);
|
text-shadow: 0 2px 8px rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- About Us Section Text Color Fix for Dark Mode --- */
|
/* --- About Us Section Text Color Fix --- */
|
||||||
html.dark #about-us h2,
|
#about-us p {
|
||||||
html.dark #about-us p {
|
color: var(--text-color) !important;
|
||||||
color: var(--heading-color) !important; /* Use a light, readable color */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --- Footer Social Icons --- */
|
||||||
|
.social-icon {
|
||||||
|
font-size: 1.5rem; /* Larger icons */
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: inline-block; /* Needed for transform */
|
||||||
|
color: #AFAFAF !important; /* Default icon color from footer */
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-icon:hover {
|
||||||
|
transform: translateY(-3px) scale(1.1); /* Lift and grow effect */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Brand Colors on Hover */
|
||||||
|
.social-icon.bi-instagram:hover { color: #E4405F !important; }
|
||||||
|
.social-icon.bi-telegram:hover { color: #26A5E4 !important; }
|
||||||
|
.social-icon.bi-whatsapp:hover { color: #25D366 !important; }
|
||||||
|
|
||||||
|
/* --- Admin Panel Sidebar --- */
|
||||||
|
body.admin-page {
|
||||||
|
padding-right: 260px; /* Make space for the sidebar */
|
||||||
|
transition: padding-right 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100vh;
|
||||||
|
width: 260px;
|
||||||
|
z-index: 1030;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: right 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar h3 {
|
||||||
|
font-family: 'Lalezar', cursive;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar .nav-link {
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 1.05rem;
|
||||||
|
padding: 15px 25px;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar .nav-link:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
border-right-color: var(--accent-color);
|
||||||
|
color: var(--white-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar .nav-link.active {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: var(--heading-color);
|
||||||
|
border-right-color: var(--accent-color);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-sidebar .sidebar-footer {
|
||||||
|
margin-top: auto;
|
||||||
|
padding: 25px;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-main-content {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Admin Layout */
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
body.admin-page {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.admin-sidebar {
|
||||||
|
right: -260px; /* Hide sidebar off-screen */
|
||||||
|
}
|
||||||
|
.admin-main-content {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
/* We'll need a hamburger menu button to toggle the sidebar on mobile */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------
|
||||||
|
# Shopping Cart Page (Dark Theme)
|
||||||
|
--------------------------------------------------------------*/
|
||||||
|
.cart-page-wrapper {
|
||||||
|
padding-top: 4rem;
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item-card {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
transition: box-shadow 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item-card:hover {
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item-image img {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item-details h5 a {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--heading-color);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item-details h5 a:hover {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-selector {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--background-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 50px;
|
||||||
|
padding: 5px;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-selector .btn {
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-selector .btn:hover {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: var(--heading-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-selector .quantity-input {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--heading-color);
|
||||||
|
width: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-price {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-item-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-item-btn .btn {
|
||||||
|
color: #888;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 0;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-item-btn .btn:hover {
|
||||||
|
color: #e74c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Order Summary Card */
|
||||||
|
.order-summary-card {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
position: sticky;
|
||||||
|
top: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-summary-card .card-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-item .label {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-item .value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-total {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-total .label {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--heading-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-total .value {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-checkout {
|
||||||
|
padding: 14px 20px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty Cart */
|
||||||
|
.empty-cart-container {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 4rem;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-cart-container .ri-shopping-cart-line {
|
||||||
|
font-size: 5rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-cart-container h2 {
|
||||||
|
font-weight: 700;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
color: var(--heading-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-cart-container p {
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 1rem auto 2rem auto;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------
|
||||||
|
# Checkout Page (Dark Theme)
|
||||||
|
--------------------------------------------------------------*/
|
||||||
|
.checkout-form-section {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 991.98px) {
|
||||||
|
.checkout-form-section {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkout-form-section h4 {
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group .form-control {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
height: 52px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group .form-control:focus {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(192, 160, 128, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group textarea.form-control {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-text-info {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--accent-color) !important;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Address Cards */
|
||||||
|
.address-card-selector .address-card {
|
||||||
|
background: var(--background-color);
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-card-selector .address-card:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-card-selector .address-card.selected {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 15px rgba(192, 160, 128, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-card-selector .form-check-input {
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
margin-top: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-card-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--heading-color);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-card-detail {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-card-icon {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#new-address-form-wrapper {
|
||||||
|
border: 2px dashed var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkout Summary */
|
||||||
|
.checkout-summary-card {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
position: sticky;
|
||||||
|
top: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkout-summary-card .list-group-item {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: var(--border-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkout-summary-card .summary-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secure-payment-info {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secure-payment-info i {
|
||||||
|
color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check-label a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.form-check-label a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*================================================
|
||||||
|
# Checkout & Auth Page Dark Theme REFINEMENTS
|
||||||
|
================================================*/
|
||||||
|
|
||||||
|
/* General text & label color fix */
|
||||||
|
body.bg-dark, .bg-dark, .checkout-form-section, .login-form-section {
|
||||||
|
/* Make sure all labels and general text are readable */
|
||||||
|
.form-label, label {
|
||||||
|
color: #EAEAEA !important; /* Use a bright, readable color */
|
||||||
|
opacity: 0.9;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
.text-muted {
|
||||||
|
color: #a0a0a0 !important; /* A lighter muted color for dark bg */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Improved Form Control Styles */
|
||||||
|
.form-control.bg-dark, .form-select.bg-dark {
|
||||||
|
color: var(--text-color) !important;
|
||||||
|
background-color: #1c1c1c !important; /* Slightly lighter than main bg */
|
||||||
|
border: 1px solid #4a4a4a;
|
||||||
|
border-radius: 8px;
|
||||||
|
height: 50px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control.bg-dark:focus, .form-select.bg-dark:focus {
|
||||||
|
background-color: #1c1c1c !important;
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 3px rgba(192, 160, 128, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.form-control.bg-dark {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for the select dropdown arrow */
|
||||||
|
.form-select.bg-dark {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23d2b48c' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox styles */
|
||||||
|
.form-check-input {
|
||||||
|
background-color: #333;
|
||||||
|
border: 1px solid #555;
|
||||||
|
}
|
||||||
|
.form-check-input:checked {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.form-check-input:focus {
|
||||||
|
box-shadow: 0 0 0 3px rgba(192, 160, 128, 0.25);
|
||||||
|
}
|
||||||
|
.form-check-label {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
.form-check-label a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.form-check-label a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Order Summary Card Refinements */
|
||||||
|
.list-group-item.bg-dark {
|
||||||
|
border-color: #444 !important; /* Darker border for list items */
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border-top-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary {
|
||||||
|
color: var(--primary-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary.btn-lg {
|
||||||
|
padding-top: 0.9rem;
|
||||||
|
padding-bottom: 0.9rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@ -1,45 +1,6 @@
|
|||||||
// Custom JavaScript will go here
|
// Custom JavaScript will go here
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
// --- Theme Toggle Functionality ---
|
|
||||||
const themeToggleButton = document.getElementById('theme-toggle');
|
|
||||||
const htmlElement = document.documentElement;
|
|
||||||
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
|
|
||||||
function applyTheme(theme) {
|
|
||||||
if (theme === 'dark') {
|
|
||||||
htmlElement.classList.add('dark');
|
|
||||||
if (themeToggleButton) themeToggleButton.innerHTML = '<i class="bi bi-sun-fill"></i>';
|
|
||||||
} else {
|
|
||||||
htmlElement.classList.remove('dark');
|
|
||||||
if (themeToggleButton) themeToggleButton.innerHTML = '<i class="bi bi-moon-fill"></i>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeTheme() {
|
|
||||||
const savedTheme = localStorage.getItem('theme');
|
|
||||||
if (savedTheme) {
|
|
||||||
applyTheme(savedTheme);
|
|
||||||
} else {
|
|
||||||
applyTheme(prefersDarkScheme.matches ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (themeToggleButton) {
|
|
||||||
themeToggleButton.addEventListener('click', () => {
|
|
||||||
const newTheme = htmlElement.classList.contains('dark') ? 'light' : 'dark';
|
|
||||||
localStorage.setItem('theme', newTheme);
|
|
||||||
applyTheme(newTheme);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
prefersDarkScheme.addEventListener('change', (e) => {
|
|
||||||
if (!localStorage.getItem('theme')) {
|
|
||||||
applyTheme(e.matches ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
initializeTheme();
|
|
||||||
|
|
||||||
// --- AOS Initialization ---
|
// --- AOS Initialization ---
|
||||||
AOS.init({
|
AOS.init({
|
||||||
@ -47,5 +8,4 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
once: true,
|
once: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
BIN
assets/pasted-20251203-190906-4bf15fd7.png
Normal file
BIN
assets/pasted-20251203-190906-4bf15fd7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
147
auth_handler.php
Normal file
147
auth_handler.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
require_once __DIR__ . '/mail/MailService.php';
|
||||||
|
|
||||||
|
$action = $_GET['action'] ?? '';
|
||||||
|
|
||||||
|
switch ($action) {
|
||||||
|
case 'send_otp':
|
||||||
|
handle_send_otp();
|
||||||
|
break;
|
||||||
|
case 'verify_otp':
|
||||||
|
handle_verify_otp();
|
||||||
|
break;
|
||||||
|
case 'logout':
|
||||||
|
handle_logout();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_send_otp() {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = filter_var(trim($_POST['email'] ?? ''), FILTER_VALIDATE_EMAIL);
|
||||||
|
if (!$email) {
|
||||||
|
flash_message('danger', 'لطفاً یک آدرس ایمیل معتبر وارد کنید.', 'login.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Generate a secure random code
|
||||||
|
$otp_code = random_int(100000, 999999);
|
||||||
|
$code_hash = password_hash((string)$otp_code, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
// OTP is valid for 10 minutes
|
||||||
|
$expires_at = date('Y-m-d H:i:s', time() + (10 * 60));
|
||||||
|
|
||||||
|
// Store the hashed code in the database
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO otp_codes (email, code_hash, expires_at) VALUES (?, ?, ?)");
|
||||||
|
$stmt->execute([$email, $code_hash, $expires_at]);
|
||||||
|
|
||||||
|
// Send the plain code to the user's email
|
||||||
|
$subject = "کد ورود شما به فروشگاه آتیمه";
|
||||||
|
$body = "<div dir='rtl' style='font-family: Vazirmatn, sans-serif; text-align: right;'><h2>کد تایید شما</h2><p>برای ورود یا ثبتنام در وبسایت آتیمه، از کد زیر استفاده کنید:</p><p style='font-size: 24px; font-weight: bold; letter-spacing: 5px; text-align: center; background: #f0f0f0; padding: 10px; border-radius: 8px;'>{$otp_code}</p><p>این کد تا ۱۰ دقیقه دیگر معتبر است.</p></div>";
|
||||||
|
|
||||||
|
$mail_result = MailService::sendMail($email, $subject, $body);
|
||||||
|
|
||||||
|
if (!$mail_result['success']) {
|
||||||
|
error_log('OTP Mail Error: ' . ($mail_result['error'] ?? 'Unknown error'));
|
||||||
|
flash_message('danger', 'خطایی در ارسال ایمیل رخ داد. لطفاً مطمئن شوید ایمیل را درست وارد کردهاید.', 'login.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store email in session to use on the verification page
|
||||||
|
$_SESSION['otp_email'] = $email;
|
||||||
|
header('Location: verify.php');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('OTP Generation Error: ' . $e->getMessage());
|
||||||
|
flash_message('danger', 'خطای سرور. لطفاً لحظاتی دیگر دوباره تلاش کنید.', 'login.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_verify_otp() {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = filter_var(trim($_POST['email'] ?? ''), FILTER_VALIDATE_EMAIL);
|
||||||
|
$otp_code = trim($_POST['otp_code'] ?? '');
|
||||||
|
|
||||||
|
if (!$email || !$otp_code) {
|
||||||
|
flash_message('danger', 'ایمیل یا کد تایید نامعتبر است.', 'login.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Find the latest, unused OTP for this email that has not expired
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM otp_codes WHERE email = ? AND is_used = 0 AND expires_at > NOW() ORDER BY created_at DESC LIMIT 1");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
$otp_row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($otp_row && password_verify($otp_code, $otp_row['code_hash'])) {
|
||||||
|
// Mark OTP as used
|
||||||
|
$stmt_update = $pdo->prepare("UPDATE otp_codes SET is_used = 1 WHERE id = ?");
|
||||||
|
$stmt_update->execute([$otp_row['id']]);
|
||||||
|
|
||||||
|
// Check if user exists
|
||||||
|
$stmt_user = $pdo->prepare("SELECT * FROM users WHERE email = ?");
|
||||||
|
$stmt_user->execute([$email]);
|
||||||
|
$user = $stmt_user->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$user_id = null;
|
||||||
|
if ($user) {
|
||||||
|
// User exists, log them in
|
||||||
|
$user_id = $user['id'];
|
||||||
|
$_SESSION['user_name'] = $user['first_name']; // Might be null, that's ok
|
||||||
|
} else {
|
||||||
|
// User does not exist, create a new one
|
||||||
|
$stmt_create = $pdo->prepare("INSERT INTO users (email, is_admin) VALUES (?, 0)");
|
||||||
|
$stmt_create->execute([$email]);
|
||||||
|
$user_id = $pdo->lastInsertId();
|
||||||
|
$_SESSION['user_name'] = null; // New user has no name yet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set session variables for login
|
||||||
|
$_SESSION['user_id'] = $user_id;
|
||||||
|
unset($_SESSION['otp_email']); // Clean up session
|
||||||
|
|
||||||
|
// Redirect to homepage with success
|
||||||
|
flash_message('success', 'شما با موفقیت وارد شدید!', 'index.php');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Invalid or expired OTP
|
||||||
|
flash_message('danger', 'کد وارد شده اشتباه یا منقضی شده است.', 'verify.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('OTP Verification Error: ' . $e->getMessage());
|
||||||
|
flash_message('danger', 'خطای سرور. لطفاً لحظاتی دیگر دوباره تلاش کنید.', 'verify.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_logout() {
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flash_message($type, $message, $location) {
|
||||||
|
// Ensure email is carried over to verify page on error
|
||||||
|
if ($location === 'verify.php' && isset($_POST['email'])) {
|
||||||
|
$_SESSION['otp_email'] = $_POST['email'];
|
||||||
|
}
|
||||||
|
$_SESSION['flash_message'] = ['type' => $type, 'message' => $message];
|
||||||
|
header("Location: $location");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
211
cart.php
211
cart.php
@ -1,121 +1,104 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
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 = 'سبد خرید';
|
$page_title = 'سبد خرید';
|
||||||
include 'includes/header.php';
|
require_once 'includes/header.php';
|
||||||
|
|
||||||
|
$cart_items = $_SESSION['cart'] ?? [];
|
||||||
|
$total_price = 0;
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="text-center mb-5">
|
<div class="cart-page-wrapper">
|
||||||
<h1 class="display-4 fw-bold">سبد خرید شما</h1>
|
<div class="container">
|
||||||
|
|
||||||
|
<?php if (empty($cart_items)): ?>
|
||||||
|
<div class="empty-cart-container">
|
||||||
|
<i class="ri-shopping-cart-line"></i>
|
||||||
|
<h2>سبد خرید شما خالی است</h2>
|
||||||
|
<p>به نظر میرسد هنوز محصولی به سبد خرید خود اضافه نکردهاید. همین حالا گشتی در فروشگاه بزنید.</p>
|
||||||
|
<a href="shop.php" class="btn btn-primary btn-lg btn-checkout">
|
||||||
|
<i class="ri-store-2-line me-2"></i>
|
||||||
|
رفتن به فروشگاه
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="text-center mb-5">
|
||||||
|
<h1 class="fw-bold display-5">سبد خرید شما</h1>
|
||||||
|
<p class="text-white-50 fs-5">جزئیات سفارش خود را بررسی و نهایی کنید.</p>
|
||||||
|
</div>
|
||||||
|
<div class="row g-5">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<?php foreach ($cart_items as $item_id => $item):
|
||||||
|
$item_total = $item['price'] * $item['quantity'];
|
||||||
|
$total_price += $item_total;
|
||||||
|
?>
|
||||||
|
<div class="cart-item-card">
|
||||||
|
<div class="remove-item-btn">
|
||||||
|
<form action="cart_handler.php" method="POST" class="d-inline">
|
||||||
|
<input type="hidden" name="product_id" value="<?php echo $item['product_id']; ?>">
|
||||||
|
<input type="hidden" name="product_color" value="<?php echo htmlspecialchars($item['color'] ?? ''); ?>">
|
||||||
|
<input type="hidden" name="action" value="remove">
|
||||||
|
<button type="submit" class="btn btn-link text-decoration-none p-0"><i class="ri-close-circle-line"></i></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="row align-items-center g-3">
|
||||||
|
<div class="col-md-2 col-3 cart-item-image">
|
||||||
|
<a href="product.php?id=<?php echo $item['product_id']; ?>">
|
||||||
|
<img src="<?php echo htmlspecialchars($item['image_url']); ?>" class="img-fluid" alt="<?php echo htmlspecialchars($item['name']); ?>">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 col-9 cart-item-details">
|
||||||
|
<h5><a href="product.php?id=<?php echo $item['product_id']; ?>"><?php echo htmlspecialchars($item['name']); ?></a></h5>
|
||||||
|
<?php if (!empty($item['color'])): ?>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<small class="text-white-50 me-2">رنگ:</small>
|
||||||
|
<span class="d-inline-block rounded-circle border" style="width: 20px; height: 20px; background-color: <?php echo htmlspecialchars($item['color']); ?>;"></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-7">
|
||||||
|
<form action="cart_handler.php" method="POST" class="quantity-selector">
|
||||||
|
<input type="hidden" name="product_id" value="<?php echo $item['product_id']; ?>">
|
||||||
|
<input type="hidden" name="product_color" value="<?php echo htmlspecialchars($item['color'] ?? ''); ?>">
|
||||||
|
<input type="hidden" name="action" value="update">
|
||||||
|
|
||||||
|
<button type="submit" name="quantity" value="<?php echo $item['quantity'] + 1; ?>" class="btn">+</button>
|
||||||
|
<input type="text" value="<?php echo $item['quantity']; ?>" class="quantity-input" readonly>
|
||||||
|
<button type="submit" name="quantity" value="<?php echo $item['quantity'] - 1; ?>" class="btn" <?php echo $item['quantity'] <= 1 ? 'disabled' : ''; ?>>-</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-5 text-end">
|
||||||
|
<span class="item-price"><?php echo number_format($item_total); ?> <small>تومان</small></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="order-summary-card">
|
||||||
|
<h4 class="card-title">خلاصه سفارش</h4>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="label">جمع کل</span>
|
||||||
|
<span class="value"><?php echo number_format($total_price); ?> تومان</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="label">هزینه ارسال</span>
|
||||||
|
<span class="value text-success">رایگان</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-total">
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="label">مبلغ نهایی</span>
|
||||||
|
<span class="value"><?php echo number_format($total_price); ?> تومان</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid mt-4">
|
||||||
|
<a href="checkout.php" class="btn btn-primary btn-lg btn-checkout">ادامه و پرداخت</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if (empty($cart_items_detailed)): ?>
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
<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'; ?>
|
|
||||||
@ -1,70 +1,77 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
// Initialize the cart if it doesn't exist
|
// Initialize cart if it doesn't exist
|
||||||
if (!isset($_SESSION['cart'])) {
|
if (!isset($_SESSION['cart'])) {
|
||||||
$_SESSION['cart'] = [];
|
$_SESSION['cart'] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the form was submitted and it's an add-to-cart action
|
// Get POST data
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_to_cart'])) {
|
$product_id = filter_input(INPUT_POST, 'product_id', FILTER_VALIDATE_INT);
|
||||||
$product_id = isset($_POST['product_id']) ? (int)$_POST['product_id'] : 0;
|
$quantity = filter_input(INPUT_POST, 'quantity', FILTER_VALIDATE_INT);
|
||||||
$quantity = isset($_POST['quantity']) ? (int)$_POST['quantity'] : 1;
|
$color = filter_input(INPUT_POST, 'product_color', FILTER_SANITIZE_STRING);
|
||||||
$color = isset($_POST['color']) ? trim($_POST['color']) : null;
|
$action = filter_input(INPUT_POST, 'action', FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
if ($product_id > 0 && $quantity > 0) {
|
if (!$action || !$product_id) {
|
||||||
// Create a unique ID for the cart item based on product ID and color
|
header('Location: shop.php');
|
||||||
$cart_item_id = $product_id . ($color ? '-' . preg_replace('/[^a-zA-Z0-9_]/ ', '-', $color) : '');
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// If the exact item (product + color) is already in the cart, update the quantity
|
// Generate a unique ID for the cart item based on product ID and color
|
||||||
|
$cart_item_id = $product_id . ($color ? '_' . str_replace('#', '', $color) : '');
|
||||||
|
|
||||||
|
switch ($action) {
|
||||||
|
case 'add':
|
||||||
|
if ($quantity > 0) {
|
||||||
|
// Check if product exists and get details
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT name, price, image_url FROM products WHERE id = ?");
|
||||||
|
$stmt->execute([$product_id]);
|
||||||
|
$product = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($product) {
|
||||||
|
// If item already in cart, update quantity
|
||||||
if (isset($_SESSION['cart'][$cart_item_id])) {
|
if (isset($_SESSION['cart'][$cart_item_id])) {
|
||||||
$_SESSION['cart'][$cart_item_id]['quantity'] += $quantity;
|
$_SESSION['cart'][$cart_item_id]['quantity'] += $quantity;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, add it as a new item
|
// Otherwise, add new item
|
||||||
$_SESSION['cart'][$cart_item_id] = [
|
$_SESSION['cart'][$cart_item_id] = [
|
||||||
'product_id' => $product_id,
|
'product_id' => $product_id,
|
||||||
|
'name' => $product['name'],
|
||||||
|
'price' => $product['price'],
|
||||||
|
'image_url' => $product['image_url'],
|
||||||
'quantity' => $quantity,
|
'quantity' => $quantity,
|
||||||
'color' => $color
|
'color' => $color
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
// Redirect to the cart page to show the updated cart
|
// Log error, maybe set a session error message to display in cart
|
||||||
header('Location: cart.php');
|
error_log("Cart Add Error: " . $e->getMessage());
|
||||||
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');
|
break;
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle updating quantities
|
case 'update':
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_cart'])){
|
if ($quantity > 0) {
|
||||||
if(!empty($_POST['quantities'])){
|
if (isset($_SESSION['cart'][$cart_item_id])) {
|
||||||
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;
|
$_SESSION['cart'][$cart_item_id]['quantity'] = $quantity;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Remove item if quantity is 0 or less
|
// If quantity is 0 or less, remove the item
|
||||||
unset($_SESSION['cart'][$cart_item_id]);
|
unset($_SESSION['cart'][$cart_item_id]);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'remove':
|
||||||
|
if (isset($_SESSION['cart'][$cart_item_id])) {
|
||||||
|
unset($_SESSION['cart'][$cart_item_id]);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
header('Location: cart.php');
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Redirect back to the cart page to show changes
|
||||||
// If someone accesses this file directly without a valid action, redirect them to the shop.
|
header('Location: cart.php');
|
||||||
header('Location: shop.php');
|
|
||||||
exit;
|
exit;
|
||||||
307
checkout.php
307
checkout.php
@ -2,173 +2,194 @@
|
|||||||
session_start();
|
session_start();
|
||||||
require_once 'db/config.php';
|
require_once 'db/config.php';
|
||||||
|
|
||||||
// If cart is empty, redirect to shop page, there is nothing to checkout
|
// Redirect if cart is empty
|
||||||
if (empty($_SESSION['cart'])) {
|
if (empty($_SESSION['cart'])) {
|
||||||
header('Location: shop.php');
|
header('Location: shop.php');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$p_title = "تسویه حساب";
|
$cart_items = $_SESSION['cart'];
|
||||||
$order_placed_successfully = false;
|
$total_price = array_reduce($cart_items, function ($sum, $item) {
|
||||||
$error_message = '';
|
return $sum + ($item['price'] * $item['quantity']);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
// Handle form submission
|
// User and address data
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
$logged_in_user = null;
|
||||||
// --- Data Validation ---
|
$user_addresses = [];
|
||||||
$name = trim($_POST['customer_name'] ?? '');
|
$is_logged_in = isset($_SESSION['user_id']);
|
||||||
$email = trim($_POST['customer_email'] ?? '');
|
|
||||||
$address = trim($_POST['customer_address'] ?? '');
|
|
||||||
|
|
||||||
if (empty($name) || empty($email) || empty($address)) {
|
if ($is_logged_in) {
|
||||||
$error_message = 'لطفاً تمام فیلدها را پر کنید.';
|
|
||||||
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
$error_message = 'لطفاً یک آدرس ایمیل معتبر وارد کنید.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(empty($error_message)) {
|
|
||||||
$pdo = db();
|
|
||||||
try {
|
try {
|
||||||
// --- Server-side recalculation of total ---
|
$pdo = db();
|
||||||
$product_ids = array_keys($_SESSION['cart']);
|
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
|
||||||
$placeholders = implode(',', array_fill(0, count($product_ids), '?'));
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
$stmt = $pdo->prepare("SELECT id, price FROM products WHERE id IN ($placeholders)");
|
$logged_in_user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
$stmt->execute($product_ids);
|
|
||||||
$products_from_db = $stmt->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_UNIQUE);
|
|
||||||
|
|
||||||
$total_amount = 0;
|
$stmt = $pdo->prepare("SELECT * FROM user_addresses WHERE user_id = ? ORDER BY is_default DESC, id DESC");
|
||||||
foreach ($_SESSION['cart'] as $product_id => $quantity) {
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
if(isset($products_from_db[$product_id])){
|
$user_addresses = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$total_amount += $products_from_db[$product_id]['price'] * $quantity;
|
} catch (PDOException $e) {
|
||||||
}
|
// In a real app, log this error
|
||||||
}
|
die("Error fetching user data.");
|
||||||
|
|
||||||
// --- 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 = 'مشکلی در ثبت سفارش شما به وجود آمد. لطفاً دوباره تلاش کنید.';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$page_title = 'تکمیل سفارش';
|
||||||
|
require_once 'includes/header.php';
|
||||||
?>
|
?>
|
||||||
<!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 -->
|
<div class="container my-5 bg-dark text-light">
|
||||||
<header class="p-3 mb-3 border-bottom border-secondary">
|
<?php
|
||||||
<div class="container">
|
if (isset($_SESSION['error_message'])) {
|
||||||
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
|
echo '<div class="alert alert-danger alert-dismissible fade show" role="alert">' . htmlspecialchars($_SESSION['error_message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||||
<a href="index.php" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
|
unset($_SESSION['error_message']);
|
||||||
<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">
|
<div class="text-center mb-5">
|
||||||
<h2 class="font-lalezar display-4"><?php echo $p_title; ?></h2>
|
<h1 class="fw-bold">تکمیل فرآیند خرید</h1>
|
||||||
|
<p class="text-muted">فقط یک قدم دیگر تا نهایی شدن سفارش شما باقیست.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row justify-content-center">
|
<form action="checkout_handler.php" method="POST">
|
||||||
<div class="col-md-8">
|
<div class="row g-5">
|
||||||
<?php if ($order_placed_successfully): ?>
|
<!-- Shipping Details -->
|
||||||
<div class="alert alert-success text-center">
|
<div class="col-lg-7">
|
||||||
<h4>از خرید شما متشکریم!</h4>
|
<h3 class="mb-4">اطلاعات ارسال</h3>
|
||||||
<p>سفارش شما با موفقیت ثبت شد و به زودی پردازش خواهد شد. یک ایمیل تایید برای شما ارسال گردید.</p>
|
|
||||||
<a href="shop.php" class="btn btn-warning">بازگشت به فروشگاه</a>
|
<?php if ($is_logged_in && !empty($user_addresses)): ?>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="saved_address" class="form-label">انتخاب آدرس</label>
|
||||||
|
<select class="form-select bg-dark text-light" id="saved_address">
|
||||||
|
<option value="">یک آدرس انتخاب کنید یا فرم زیر را پر کنید...</option>
|
||||||
|
<?php foreach ($user_addresses as $addr): ?>
|
||||||
|
<option value='<?php echo json_encode($addr, JSON_HEX_APOS | JSON_HEX_QUOT); ?>'>
|
||||||
|
<?php echo htmlspecialchars($addr['province'] . '، ' . $addr['city'] . '، ' . $addr['address_line']); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
|
||||||
<?php if (!empty($error_message)): ?>
|
|
||||||
<div class="alert alert-danger">.<?php echo $error_message; ?></div>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div class="card bg-dark-2">
|
|
||||||
|
<div class="card bg-dark border-secondary shadow-sm rounded-4">
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<h5 class="card-title mb-4">اطلاعات ارسال</h5>
|
<div class="row g-3">
|
||||||
<form action="checkout.php" method="POST">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<label for="first_name" class="form-label">نام</label>
|
||||||
<label for="customer_name" class="form-label">نام و نام خانوادگی</label>
|
<input type="text" class="form-control bg-dark text-light" id="first_name" name="first_name" value="<?php echo htmlspecialchars($logged_in_user['first_name'] ?? ''); ?>" required>
|
||||||
<input type="text" class="form-control bg-dark text-white" id="customer_name" name="customer_name" required>
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="last_name" class="form-label">نام خانوادگی</label>
|
||||||
|
<input type="text" class="form-control bg-dark text-light" id="last_name" name="last_name" value="<?php echo htmlspecialchars($logged_in_user['last_name'] ?? ''); ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="phone_number" class="form-label">تلفن همراه</label>
|
||||||
|
<input type="tel" class="form-control bg-dark text-light" id="phone_number" name="phone_number" value="<?php echo htmlspecialchars($logged_in_user['phone_number'] ?? ''); ?>" required>
|
||||||
|
<?php if (!$is_logged_in): ?>
|
||||||
|
<div class="form-text text-info fw-bold">توجه: فقط با شماره تلفن همراه میتوان سفارش را رهگیری کرد.</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="province" class="form-label">استان</label>
|
||||||
|
<input type="text" class="form-control bg-dark text-light" id="province" name="province" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="city" class="form-label">شهر</label>
|
||||||
|
<input type="text" class="form-control bg-dark text-light" id="city" name="city" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="address_line" class="form-label">آدرس دقیق</label>
|
||||||
|
<textarea class="form-control bg-dark text-light" id="address_line" name="address_line" rows="2" required></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<label for="postal_code" class="form-label">کد پستی</label>
|
||||||
|
<input type="text" class="form-control bg-dark text-light" id="postal_code" name="postal_code" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-7">
|
||||||
|
<label for="email" class="form-label">ایمیل (اختیاری)</label>
|
||||||
|
<input type="email" class="form-control bg-dark text-light" id="email" name="email" value="<?php echo htmlspecialchars($logged_in_user['email'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mt-4">
|
||||||
|
<input type="checkbox" class="form-check-input" id="terms" name="terms" required>
|
||||||
|
<label class="form-check-label" for="terms">
|
||||||
|
با <a href="#">قوانین و مقررات</a> سایت موافقم.
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Order Summary -->
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<div class="card bg-dark border-secondary shadow-sm rounded-4 sticky-top" style="top: 100px;">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<h4 class="card-title fw-bold mb-4">خلاصه سفارش</h4>
|
||||||
|
<ul class="list-group list-group-flush mb-4">
|
||||||
|
<?php foreach($cart_items as $item): ?>
|
||||||
|
<li class="list-group-item bg-dark text-light d-flex justify-content-between align-items-center px-0">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<img src="<?php echo htmlspecialchars($item['image_url']); ?>" width="60" class="rounded-3 me-3" alt="<?php echo htmlspecialchars($item['name']); ?>">
|
||||||
|
<div>
|
||||||
|
<?php echo htmlspecialchars($item['name']); ?>
|
||||||
|
<small class="d-block text-muted">تعداد: <?php echo $item['quantity']; ?></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="fw-bold"><?php echo number_format($item['price'] * $item['quantity']); ?></span>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mb-2">
|
||||||
|
<span class="text-muted">جمع کل</span>
|
||||||
|
<span><?php echo number_format($total_price); ?> تومان</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between mb-3">
|
||||||
|
<span class="text-muted">هزینه ارسال</span>
|
||||||
|
<span class="text-success">رایگان</span>
|
||||||
|
</div>
|
||||||
|
<hr class="border-secondary">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<span class="h5 fw-bold">مبلغ نهایی</span>
|
||||||
|
<span class="h5 fw-bolder text-primary"><?php echo number_format($total_price); ?> تومان</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg">ثبت نهایی سفارش</button>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<small class="text-muted"><i class="ri-lock-line me-1"></i> پرداخت امن و مطمئن</small>
|
||||||
|
</div>
|
||||||
</div>
|
</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>
|
||||||
<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>
|
||||||
<div class="d-grid mt-4">
|
|
||||||
<button type="submit" class="btn btn-warning btn-lg fw-bold">ثبت سفارش و پرداخت</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
<script>
|
||||||
<footer class="py-5 mt-5 border-top border-secondary">
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
<div class="container text-center">
|
const savedAddressSelect = document.getElementById('saved_address');
|
||||||
<p class="text-muted">© <?php echo date("Y"); ?> چرم آتیمه. تمام حقوق محفوظ است.</p>
|
if (savedAddressSelect) {
|
||||||
</div>
|
savedAddressSelect.addEventListener('change', function() {
|
||||||
</footer>
|
if (this.value) {
|
||||||
|
try {
|
||||||
|
const address = JSON.parse(this.value);
|
||||||
|
document.getElementById('province').value = address.province || '';
|
||||||
|
document.getElementById('city').value = address.city || '';
|
||||||
|
document.getElementById('address_line').value = address.address_line || '';
|
||||||
|
document.getElementById('postal_code').value = address.postal_code || '';
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to parse address JSON:", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Clear fields if no address is selected
|
||||||
|
document.getElementById('province').value = '';
|
||||||
|
document.getElementById('city').value = '';
|
||||||
|
document.getElementById('address_line').value = '';
|
||||||
|
document.getElementById('postal_code').value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
100
checkout_handler.php
Normal file
100
checkout_handler.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// 1. Basic Security: Only allow POST requests and check for cart
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($_SESSION['cart'])) {
|
||||||
|
header('Location: shop.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Retrieve and sanitize form data
|
||||||
|
$first_name = filter_input(INPUT_POST, 'first_name', FILTER_SANITIZE_STRING);
|
||||||
|
$last_name = filter_input(INPUT_POST, 'last_name', FILTER_SANITIZE_STRING);
|
||||||
|
$phone_number = filter_input(INPUT_POST, 'phone_number', FILTER_SANITIZE_STRING);
|
||||||
|
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
|
||||||
|
$province = filter_input(INPUT_POST, 'province', FILTER_SANITIZE_STRING);
|
||||||
|
$city = filter_input(INPUT_POST, 'city', FILTER_SANITIZE_STRING);
|
||||||
|
$address_line = filter_input(INPUT_POST, 'address_line', FILTER_SANITIZE_STRING);
|
||||||
|
$postal_code = filter_input(INPUT_POST, 'postal_code', FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
|
// 3. Server-side validation (Email is now optional)
|
||||||
|
if (!$first_name || !$last_name || !$phone_number || !$province || !$city || !$address_line || !$postal_code) {
|
||||||
|
$_SESSION['error_message'] = 'لطفاً تمام فیلدهای آدرس به جز ایمیل را تکمیل کنید.';
|
||||||
|
header('Location: checkout.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 4. Prepare order data
|
||||||
|
$billing_name = trim($first_name . ' ' . $last_name);
|
||||||
|
$cart_items = $_SESSION['cart'];
|
||||||
|
$total_amount = array_reduce($cart_items, function ($sum, $item) {
|
||||||
|
return $sum + ($item['price'] * $item['quantity']);
|
||||||
|
}, 0);
|
||||||
|
$items_json = json_encode($cart_items, JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
// 5. Insert the order into the database using the correct, updated column names
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
"INSERT INTO orders (user_id, billing_name, billing_email, customer_phone, shipping_province, shipping_city, shipping_address_line, shipping_postal_code, total_amount, items_json, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
|
||||||
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
||||||
|
$status = 'Pending'; // Default status
|
||||||
|
$final_email = ($email !== false && $email !== '') ? $email : null; // Ensure email is null if empty/invalid, not false
|
||||||
|
|
||||||
|
$stmt->execute([
|
||||||
|
$user_id,
|
||||||
|
$billing_name,
|
||||||
|
$final_email,
|
||||||
|
$phone_number,
|
||||||
|
$province,
|
||||||
|
$city,
|
||||||
|
$address_line,
|
||||||
|
$postal_code,
|
||||||
|
$total_amount,
|
||||||
|
$items_json,
|
||||||
|
$status
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 6. If user is logged in, save the new address for future use
|
||||||
|
if ($user_id) {
|
||||||
|
$stmt_check_addr = $pdo->prepare("SELECT COUNT(*) FROM user_addresses WHERE user_id = ? AND address_line = ? AND postal_code = ?");
|
||||||
|
$stmt_check_addr->execute([$user_id, $address_line, $postal_code]);
|
||||||
|
$address_exists = $stmt_check_addr->fetchColumn();
|
||||||
|
|
||||||
|
if ($address_exists == 0) {
|
||||||
|
$stmt_save_addr = $pdo->prepare(
|
||||||
|
"INSERT INTO user_addresses (user_id, first_name, last_name, phone_number, province, city, address_line, postal_code) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
$stmt_save_addr->execute([
|
||||||
|
$user_id, $first_name, $last_name, $phone_number, $province, $city, $address_line, $postal_code
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Commit transaction
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
// 8. Clear the cart and redirect with a success message
|
||||||
|
unset($_SESSION['cart']);
|
||||||
|
$_SESSION['success_message'] = 'سفارش شما با موفقیت ثبت شد! از خرید شما متشکریم.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// 9. If anything fails, rollback and redirect with an error
|
||||||
|
$pdo->rollBack();
|
||||||
|
error_log("Order Creation Failed: " . $e->getMessage()); // Log error for admin
|
||||||
|
$_SESSION['error_message'] = 'خطایی در ثبت سفارش رخ داد. لطفاً دوباره تلاش کنید.';
|
||||||
|
header('Location: checkout.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
112
contact.php
112
contact.php
@ -1,73 +1,69 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start(); // Ensure session is started
|
||||||
require_once __DIR__ . '/mail/MailService.php';
|
|
||||||
|
|
||||||
$page_title = 'تماس با ما';
|
$page_title = 'تماس با ما';
|
||||||
$page_description = 'با ما در تماس باشید. نظرات و پیشنهادات شما برای ما ارزشمند است.';
|
require_once 'includes/header.php';
|
||||||
|
require_once 'mail/MailService.php';
|
||||||
|
|
||||||
$message = '';
|
// Handle form submission
|
||||||
$error = '';
|
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['contact_form'])) {
|
||||||
|
$name = trim(filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING));
|
||||||
|
$email = trim(filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL));
|
||||||
|
$message = trim(filter_input(INPUT_POST, 'message', FILTER_SANITIZE_STRING));
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
if (empty($name) || empty($email) || empty($message)) {
|
||||||
$name = trim($_POST['name']);
|
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'لطفاً تمام فیلدها را پر کنید.'];
|
||||||
$email = trim($_POST['email']);
|
} elseif (!$email) {
|
||||||
$subject = trim($_POST['subject']);
|
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'آدرس ایمیل وارد شده معتبر نیست.'];
|
||||||
$body = trim($_POST['message']);
|
|
||||||
|
|
||||||
if (empty($name) || empty($email) || empty($subject) || empty($body) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
$error = 'لطفاً تمام فیلدها را به درستی پر کنید.';
|
|
||||||
} else {
|
} else {
|
||||||
$response = MailService::sendContactMessage($name, $email, $body, null, $subject);
|
// Send email using MailService
|
||||||
if (!empty($response['success'])) {
|
$to_email = getenv('MAIL_TO') ?: 'your-default-email@example.com'; // Fallback email
|
||||||
$message = 'پیام شما با موفقیت ارسال شد. سپاسگزاریم!';
|
$subject = "پیام جدید از فرم تماس وبسایت";
|
||||||
|
|
||||||
|
$email_result = MailService::sendContactMessage($name, $email, $message, $to_email, $subject);
|
||||||
|
|
||||||
|
if (!empty($email_result['success'])) {
|
||||||
|
$_SESSION['flash_message'] = ['type' => 'success', 'message' => 'پیام شما با موفقیت ارسال شد. سپاسگزاریم!'];
|
||||||
} else {
|
} else {
|
||||||
$error = 'خطایی در ارسال پیام رخ داد. لطفاً بعداً تلاش کنید. متن خطا: ' . htmlspecialchars($response['error'] ?? 'Unknown error');
|
$_SESSION['flash_message'] = ['type' => 'error', 'message' => 'خطا در ارسال پیام. لطفاً بعداً دوباره تلاش کنید.'];
|
||||||
|
error_log("MailService Error: " . ($email_result['error'] ?? 'Unknown error'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Redirect to the same page to prevent form resubmission
|
||||||
|
header("Location: contact.php");
|
||||||
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
include 'includes/header.php';
|
// Check for flash messages
|
||||||
|
$flash_message = $_SESSION['flash_message'] ?? null;
|
||||||
|
if ($flash_message) {
|
||||||
|
unset($_SESSION['flash_message']);
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<main class="container py-5">
|
||||||
<div class="row justify-content-center">
|
<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="col-lg-8">
|
||||||
<div class="card border-0" style="background-color: var(--surface-color);">
|
<div class="text-center mb-5" data-aos="fade-down">
|
||||||
|
<h1 class="display-4 fw-bold">ارتباط با ما</h1>
|
||||||
|
<p class="fs-5 text-muted">نظرات، پیشنهادات و سوالات شما برای ما ارزشمند است.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border-0 shadow-lg" data-aos="fade-up">
|
||||||
<div class="card-body p-4 p-md-5">
|
<div class="card-body p-4 p-md-5">
|
||||||
|
<form action="contact.php" method="POST">
|
||||||
<?php if ($message): ?>
|
<input type="hidden" name="contact_form">
|
||||||
<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">
|
<div class="mb-4">
|
||||||
<label for="name" class="form-label fs-5">نام شما</label>
|
<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>
|
<input type="text" class="form-control form-control-lg bg-dark" id="name" name="name" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="email" class="form-label fs-5">ایمیل شما</label>
|
<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>
|
<input type="email" class="form-control form-control-lg bg-dark" 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>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="message" class="form-label fs-5">پیام شما</label>
|
<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>
|
<textarea class="form-control form-control-lg bg-dark" id="message" name="message" rows="6" required></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<button type="submit" class="btn btn-primary btn-lg">ارسال پیام</button>
|
<button type="submit" class="btn btn-primary btn-lg">ارسال پیام</button>
|
||||||
@ -75,8 +71,24 @@ include 'includes/header.php';
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-info mt-4"><b>توجه:</b> این فرم برای اهداف آزمایشی است. برای دریافت واقعی ایمیلها، باید اطلاعات سرور ایمیل (SMTP) خود را در فایل <code>.env</code> وارد کنید.</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
<?php include 'includes/footer.php'; ?>
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
<?php if ($flash_message): ?>
|
||||||
|
Swal.fire({
|
||||||
|
title: '<?php echo $flash_message["type"] === "success" ? "موفق" : "خطا"; ?>',
|
||||||
|
text: '<?php echo addslashes($flash_message["message"]); ?>',
|
||||||
|
icon: '<?php echo $flash_message["type"]; ?>',
|
||||||
|
confirmButtonText: 'باشه',
|
||||||
|
background: '#2C2C2C',
|
||||||
|
color: '#D5D5D5'
|
||||||
|
});
|
||||||
|
<?php endif; ?>
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
ALTER TABLE products ADD COLUMN colors VARCHAR(255) DEFAULT NULL COMMENT 'Comma-separated list of available colors';
|
-- Add the colors column to the products table if it doesn't exist
|
||||||
|
ALTER TABLE `products` ADD COLUMN IF NOT EXISTS `colors` VARCHAR(255) DEFAULT NULL COMMENT 'Comma-separated list of available colors';
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
ALTER TABLE products ADD COLUMN is_featured BOOLEAN DEFAULT 0;
|
-- Add the is_featured column to the products table if it doesn't exist
|
||||||
|
ALTER TABLE `products` ADD COLUMN IF NOT EXISTS `is_featured` BOOLEAN DEFAULT 0;
|
||||||
|
|||||||
13
db/migrations/003_create_orders_table.sql
Normal file
13
db/migrations/003_create_orders_table.sql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `orders` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`customer_name` VARCHAR(255) NOT NULL,
|
||||||
|
`customer_email` VARCHAR(255) NOT NULL,
|
||||||
|
`customer_address` TEXT NOT NULL,
|
||||||
|
`customer_phone` VARCHAR(50) DEFAULT NULL,
|
||||||
|
`total_amount` DECIMAL(10, 2) NOT NULL,
|
||||||
|
`items_json` JSON NOT NULL,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- Add the status column to the orders table if it doesn't exist
|
||||||
|
ALTER TABLE `orders` ADD COLUMN IF NOT EXISTS `status` VARCHAR(50) NOT NULL DEFAULT 'Pending' COMMENT 'e.g., Pending, Processing, Shipped, Delivered, Canceled';
|
||||||
11
db/migrations/004_create_users_table.sql
Normal file
11
db/migrations/004_create_users_table.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-- Create users table to store customer information
|
||||||
|
CREATE TABLE IF NOT EXISTS `users` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`first_name` VARCHAR(100) NOT NULL,
|
||||||
|
`last_name` VARCHAR(100) NOT NULL,
|
||||||
|
`email` VARCHAR(150) NOT NULL UNIQUE,
|
||||||
|
`phone_number` VARCHAR(20) DEFAULT NULL UNIQUE,
|
||||||
|
`password` VARCHAR(255) NOT NULL,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
12
db/migrations/005_create_user_addresses_table.sql
Normal file
12
db/migrations/005_create_user_addresses_table.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
-- Create user_addresses table to store customer shipping addresses
|
||||||
|
CREATE TABLE IF NOT EXISTS `user_addresses` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`user_id` INT NOT NULL,
|
||||||
|
`province` VARCHAR(100) NOT NULL,
|
||||||
|
`city` VARCHAR(100) NOT NULL,
|
||||||
|
`address_line` TEXT NOT NULL,
|
||||||
|
`postal_code` VARCHAR(20) NOT NULL,
|
||||||
|
`is_default` BOOLEAN DEFAULT FALSE,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
2
db/migrations/006_add_is_admin_to_users.sql
Normal file
2
db/migrations/006_add_is_admin_to_users.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
-- Add is_admin flag to users table to differentiate admins from regular users
|
||||||
|
ALTER TABLE `users` ADD `is_admin` BOOLEAN NOT NULL DEFAULT FALSE AFTER `password`;
|
||||||
18
db/migrations/007_update_orders_table.sql
Normal file
18
db/migrations/007_update_orders_table.sql
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
-- Update orders table to support structured addresses and link to users
|
||||||
|
|
||||||
|
-- Add user_id to link orders to the users table (can be NULL for guest checkouts)
|
||||||
|
ALTER TABLE `orders` ADD COLUMN `user_id` INT NULL DEFAULT NULL AFTER `id`, ADD CONSTRAINT `fk_orders_users` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
-- Add structured shipping address fields
|
||||||
|
ALTER TABLE `orders`
|
||||||
|
ADD COLUMN `shipping_province` VARCHAR(100) NOT NULL AFTER `customer_phone`,
|
||||||
|
ADD COLUMN `shipping_city` VARCHAR(100) NOT NULL AFTER `shipping_province`,
|
||||||
|
ADD COLUMN `shipping_address_line` TEXT NOT NULL AFTER `shipping_city`,
|
||||||
|
ADD COLUMN `shipping_postal_code` VARCHAR(20) NOT NULL AFTER `shipping_address_line`;
|
||||||
|
|
||||||
|
-- Rename old columns to avoid confusion, but keep them for any old data
|
||||||
|
ALTER TABLE `orders`
|
||||||
|
CHANGE `customer_name` `billing_name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||||
|
CHANGE `customer_email` `billing_email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||||
|
CHANGE `customer_address` `legacy_customer_address` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;
|
||||||
|
|
||||||
9
db/migrations/008_create_otp_table.sql
Normal file
9
db/migrations/008_create_otp_table.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `otp_codes` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`email` VARCHAR(255) NOT NULL,
|
||||||
|
`code_hash` VARCHAR(255) NOT NULL,
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`expires_at` TIMESTAMP NOT NULL,
|
||||||
|
`is_used` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
INDEX `email_index` (`email`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
@ -37,9 +37,9 @@
|
|||||||
<h5 class="fw-bold mb-3">ما را دنبال کنید</h5>
|
<h5 class="fw-bold mb-3">ما را دنبال کنید</h5>
|
||||||
<p class="text-white-50">از جدیدترین محصولات و تخفیفها باخبر شوید.</p>
|
<p class="text-white-50">از جدیدترین محصولات و تخفیفها باخبر شوید.</p>
|
||||||
<div class="d-flex mt-3 social-icons">
|
<div class="d-flex mt-3 social-icons">
|
||||||
<a href="#" class="btn btn-outline-primary me-2"><i class="bi bi-instagram"></i></a>
|
<a href="#" class="social-icon me-3"><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="social-icon me-3"><i class="bi bi-telegram"></i></a>
|
||||||
<a href="#" class="btn btn-outline-primary"><i class="bi bi-whatsapp"></i></a>
|
<a href="#" class="social-icon"><i class="bi bi-whatsapp"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -20,8 +20,9 @@ $page_title = $page_title ?? 'فروشگاه آتیمه'; // Default title
|
|||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
||||||
<!-- Bootstrap Icons -->
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<!-- Remix Icon CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.2.0/fonts/remixicon.css" rel="stylesheet" />
|
||||||
|
|
||||||
<!-- AOS CSS -->
|
<!-- AOS CSS -->
|
||||||
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
|
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
|
||||||
@ -67,7 +68,7 @@ $page_title = $page_title ?? 'فروشگاه آتیمه'; // Default title
|
|||||||
<a class="nav-link" href="shop.php">فروشگاه</a>
|
<a class="nav-link" href="shop.php">فروشگاه</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">درباره ما</a>
|
<a class="nav-link" href="about.php">درباره ما</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="contact.php">تماس با ما</a>
|
<a class="nav-link" href="contact.php">تماس با ما</a>
|
||||||
@ -75,11 +76,8 @@ $page_title = $page_title ?? 'فروشگاه آتیمه'; // Default title
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<button id="theme-toggle" class="btn me-3">
|
|
||||||
<i class="bi bi-moon-stars-fill"></i>
|
|
||||||
</button>
|
|
||||||
<a href="cart.php" class="ms-4 position-relative">
|
<a href="cart.php" class="ms-4 position-relative">
|
||||||
<i class="bi bi-bag fs-5"></i>
|
<i class="ri-shopping-bag-line fs-5"></i>
|
||||||
<?php if ($cart_item_count > 0): ?>
|
<?php if ($cart_item_count > 0): ?>
|
||||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
|
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
|
||||||
<?php echo $cart_item_count; ?>
|
<?php echo $cart_item_count; ?>
|
||||||
@ -87,9 +85,25 @@ $page_title = $page_title ?? 'فروشگاه آتیمه'; // Default title
|
|||||||
</span>
|
</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</a>
|
</a>
|
||||||
<a href="/admin/login.php" class="ms-3">
|
|
||||||
<i class="bi bi-person fs-5"></i>
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<div class="dropdown ms-3">
|
||||||
|
<a href="#" class="d-flex align-items-center text-decoration-none dropdown-toggle" id="dropdownUser" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="ri-user-line fs-5 me-1"></i>
|
||||||
|
<span><?php echo htmlspecialchars($_SESSION['user_name']); ?></span>
|
||||||
</a>
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end text-small shadow" aria-labelledby="dropdownUser">
|
||||||
|
<li><a class="dropdown-item" href="profile.php">حساب کاربری</a></li>
|
||||||
|
<?php if (!empty($_SESSION['is_admin'])): ?>
|
||||||
|
<li><a class="dropdown-item" href="/admin/index.php">پنل مدیریت</a></li>
|
||||||
|
<?php endif; ?>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><a class="dropdown-item" href="logout.php">خروج</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="login.php" class="btn btn-primary btn-sm ms-3">ورود / ثبتنام</a>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
12
index.php
12
index.php
@ -22,9 +22,19 @@ include 'includes/header.php';
|
|||||||
<!-- Featured Products Section -->
|
<!-- Featured Products Section -->
|
||||||
<section id="featured-products" class="py-5">
|
<section id="featured-products" class="py-5">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<?php
|
||||||
|
if (isset($_SESSION['success_message'])) {
|
||||||
|
echo '<div class="alert alert-success alert-dismissible fade show" role="alert">' . $_SESSION['success_message'] . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||||
|
unset($_SESSION['success_message']);
|
||||||
|
}
|
||||||
|
if (isset($_SESSION['error_message'])) {
|
||||||
|
echo '<div class="alert alert-danger alert-dismissible fade show" role="alert">' . $_SESSION['error_message'] . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||||
|
unset($_SESSION['error_message']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
<div class="text-center mb-5" data-aos="fade-up">
|
<div class="text-center mb-5" data-aos="fade-up">
|
||||||
<h2 class="display-5 fw-bold">مجموعه برگزیده ما</h2>
|
<h2 class="display-5 fw-bold">مجموعه برگزیده ما</h2>
|
||||||
<p class="text-muted fs-5">دستچین شده برای سلیقههای خاص.</p>
|
<p class="fs-5">دستچین شده برای سلیقههای خاص.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 g-lg-5">
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 g-lg-5">
|
||||||
<?php
|
<?php
|
||||||
|
|||||||
84
login.php
Normal file
84
login.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$page_title = "ورود یا ثبتنام";
|
||||||
|
// We don't include the standard header/footer as this is a standalone page design
|
||||||
|
?>
|
||||||
|
<!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 $page_title; ?> - آتیمه</title>
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
||||||
|
<!-- Vazirmatn Font -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet" type="text/css" />
|
||||||
|
<!-- Main Custom CSS (for variables) -->
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<!-- Custom Auth CSS -->
|
||||||
|
<link rel="stylesheet" href="assets/css/auth_style.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<!-- Left Side: Background Image and Branding -->
|
||||||
|
<div class="auth-bg">
|
||||||
|
<div class="auth-bg-content">
|
||||||
|
<h1>به خانه چرم بازگردید</h1>
|
||||||
|
<p>اصالت و زیبایی در دستان شما. برای ورود یا ساخت حساب کاربری، ایمیل خود را وارد کنید.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right Side: Form -->
|
||||||
|
<div class="auth-form-wrapper">
|
||||||
|
<div class="auth-form-container">
|
||||||
|
<div class="form-header text-center">
|
||||||
|
<h2>ورود یا ثبتنام</h2>
|
||||||
|
<p>برای دریافت کد یکبار مصرف، ایمیل خود را وارد کنید.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if(isset($_SESSION['flash_message'])): ?>
|
||||||
|
<div class="alert alert-<?php echo $_SESSION['flash_message']['type']; ?> alert-dismissible fade show" role="alert">
|
||||||
|
<?php echo htmlspecialchars($_SESSION['flash_message']['message']); ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php unset($_SESSION['flash_message']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form action="auth_handler.php?action=send_otp" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="form-label visually-hidden">ایمیل</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" placeholder="ایمیل خود را وارد کنید" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-grid mt-4">
|
||||||
|
<button type="submit" class="btn btn-primary">ادامه با ایمیل</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="separator my-4"><span>یا</span></div>
|
||||||
|
|
||||||
|
<div class="d-grid">
|
||||||
|
<button class="btn btn-google" disabled>
|
||||||
|
<svg class="me-2" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 48 48"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/><path fill="none" d="M0 0h48v48H0z"/></svg>
|
||||||
|
ورود با گوگل (به زودی)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="auth-footer">
|
||||||
|
<p><a href="index.php">بازگشت به صفحه اصلی</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap JS -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
logout.php
Normal file
3
logout.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php
|
||||||
|
// This file provides a simple entry point for logging out.
|
||||||
|
require_once __DIR__ . '/auth_handler.php';
|
||||||
85
product.php
85
product.php
@ -1,10 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
|
||||||
require_once 'db/config.php';
|
require_once 'db/config.php';
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
|
||||||
$product_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
$product_id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
|
||||||
|
$product = null;
|
||||||
|
$db_error = '';
|
||||||
|
|
||||||
if ($product_id <= 0) {
|
if (!$product_id) {
|
||||||
|
// Redirect or show error if ID is not valid
|
||||||
header("Location: shop.php");
|
header("Location: shop.php");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@ -14,62 +17,76 @@ try {
|
|||||||
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
|
||||||
$stmt->execute([$product_id]);
|
$stmt->execute([$product_id]);
|
||||||
$product = $stmt->fetch(PDO::FETCH_ASSOC);
|
$product = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (!$product) {
|
|
||||||
header("Location: shop.php");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
error_log("DB Error: " . $e->getMessage());
|
error_log("Database Error: " . $e->getMessage());
|
||||||
die("An error occurred. Please try again later.");
|
$db_error = "<p>خطا در برقراری ارتباط با پایگاه داده.</p>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$page_title = htmlspecialchars($product['name']);
|
// If product not found, show a message and stop
|
||||||
$available_colors = !empty($product['colors']) ? array_map('trim', explode(',', $product['colors'])) : [];
|
if (!$product) {
|
||||||
|
echo '<main class="container py-5 text-center"><div class="alert alert-danger">محصولی با این شناسه یافت نشد.</div><div><a href="shop.php" class="btn btn-primary mt-3">بازگشت به فروشگاه</a></div></main>';
|
||||||
|
require_once 'includes/footer.php';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set page title after fetching product name
|
||||||
|
$page_title = htmlspecialchars($product['name']);
|
||||||
|
|
||||||
|
// Safely decode colors JSON
|
||||||
|
$available_colors = json_decode($product['colors'] ?? '[]', true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
$available_colors = []; // Assign empty array if JSON is invalid
|
||||||
|
}
|
||||||
|
|
||||||
include 'includes/header.php';
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<main class="container py-5">
|
||||||
<div class="row g-5">
|
<div class="row g-5">
|
||||||
<div class="col-lg-6" data-aos="zoom-in">
|
<div class="col-lg-6" data-aos="fade-right">
|
||||||
<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 class="product-image-gallery">
|
||||||
|
<img src="<?php echo htmlspecialchars($product['image_url']); ?>" class="img-fluid rounded-4 shadow-lg" alt="<?php echo htmlspecialchars($product['name']); ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6 d-flex flex-column justify-content-center">
|
|
||||||
<div data-aos="fade-right" data-aos-delay="100">
|
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
<div data-aos="fade-up" data-aos-delay="200">
|
<div class="col-lg-6" data-aos="fade-left">
|
||||||
<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>
|
<h1 class="display-5 fw-bold mb-3"><?php echo htmlspecialchars($product['name']); ?></h1>
|
||||||
|
|
||||||
|
<div class="d-flex align-items-center mb-4">
|
||||||
|
<p class="display-6 text-primary fw-bold m-0"><?php echo number_format($product['price']); ?> تومان</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form action="cart_handler.php" method="POST" data-aos="fade-up" data-aos-delay="300">
|
<p class="fs-5 mb-4"><?php echo nl2br(htmlspecialchars($product['description'])); ?></p>
|
||||||
|
|
||||||
|
<form action="cart_handler.php" method="POST">
|
||||||
<input type="hidden" name="product_id" value="<?php echo $product['id']; ?>">
|
<input type="hidden" name="product_id" value="<?php echo $product['id']; ?>">
|
||||||
|
<input type="hidden" name="action" value="add">
|
||||||
|
|
||||||
<?php if (!empty($available_colors)): ?>
|
<?php if (!empty($available_colors)): ?>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="form-label fw-bold fs-5 mb-3">انتخاب رنگ:</label>
|
<h5 class="mb-3">انتخاب رنگ:</h5>
|
||||||
<div class="d-flex flex-wrap gap-3 color-swatches">
|
<div class="color-swatches">
|
||||||
<?php foreach ($available_colors as $index => $color): ?>
|
<?php foreach ($available_colors as $index => $color_hex): ?>
|
||||||
<div data-bs-toggle="tooltip" title="<?php echo htmlspecialchars($color); ?>">
|
<input type="radio" class="btn-check" name="product_color" id="color_<?php echo $index; ?>" value="<?php echo htmlspecialchars($color_hex); ?>" <?php echo ($index === 0) ? 'checked' : ''; ?>>
|
||||||
<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; ?>" style="background-color: <?php echo htmlspecialchars($color_hex); ?>;" title="<?php echo htmlspecialchars($color_hex); ?>"></label>
|
||||||
<label class="btn" for="color-<?php echo $index; ?>"><?php echo htmlspecialchars($color); ?></label>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="d-flex align-items-center mb-4">
|
<div class="row align-items-center mb-4">
|
||||||
<label for="quantity" class="form-label ms-3 mb-0 fs-5">تعداد:</label>
|
<div class="col-md-5 col-lg-4">
|
||||||
<input type="number" name="quantity" id="quantity" class="form-control bg-dark text-white" value="1" min="1" max="10" style="width: 80px;">
|
<label for="quantity" class="form-label fw-bold">تعداد:</label>
|
||||||
|
<input type="number" name="quantity" id="quantity" class="form-control form-control-lg bg-dark text-center" value="1" min="1" max="10">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" name="add_to_cart" class="btn btn-primary btn-lg w-100 py-3 fw-bold">افزودن به سبد خرید</button>
|
<div class="d-grid gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg"><i class="fas fa-shopping-cart me-2"></i> افزودن به سبد خرید</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
<?php include 'includes/footer.php'; ?>
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
119
profile.php
Normal file
119
profile.php
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Require user to be logged in
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
// Fetch user data
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Fetch user addresses
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM user_addresses WHERE user_id = ? ORDER BY is_default DESC, id DESC");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$addresses = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Error fetching user data: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$page_title = 'حساب کاربری من';
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.profile-nav .nav-link {
|
||||||
|
color: #6c757d;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
}
|
||||||
|
.profile-nav .nav-link.active {
|
||||||
|
color: var(--bs-primary);
|
||||||
|
border-bottom-color: var(--bs-primary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container my-5">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center p-3">
|
||||||
|
<i class="ri-user-smile-line fs-1 text-primary mb-3"></i>
|
||||||
|
<h4 class="card-title"><?php echo htmlspecialchars($user['first_name'] . ' ' . $user['last_name']); ?></h4>
|
||||||
|
<p class="text-muted"><?php echo htmlspecialchars($user['email']); ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="card mt-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3">افزودن آدرس جدید</h5>
|
||||||
|
<form action="#" method="POST"> <!-- Will be handled by a future handler -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="province" class="form-label">استان</label>
|
||||||
|
<input type="text" class="form-control" id="province" name="province" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="city" class="form-label">شهر</label>
|
||||||
|
<input type="text" class="form-control" id="city" name="city" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="address_line" class="form-label">آدرس دقیق</label>
|
||||||
|
<textarea class="form-control" id="address_line" name="address_line" rows="3" required></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="postal_code" class="form-label">کد پستی</label>
|
||||||
|
<input type="text" class="form-control" id="postal_code" name="postal_code" required>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-secondary">ثبت آدرس</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0">آدرسهای من</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if (empty($addresses)): ?>
|
||||||
|
<p class="text-center text-muted">شما هنوز هیچ آدرسی ثبت نکردهاید.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="list-group">
|
||||||
|
<?php foreach ($addresses as $address): ?>
|
||||||
|
<div class="list-group-item list-group-item-action flex-column align-items-start">
|
||||||
|
<div class="d-flex w-100 justify-content-between">
|
||||||
|
<h6 class="mb-1">
|
||||||
|
<?php echo htmlspecialchars($address['province'] . '، ' . $address['city']); ?>
|
||||||
|
<?php if($address['is_default']): ?>
|
||||||
|
<span class="badge bg-primary">پیشفرض</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</h6>
|
||||||
|
<small class="text-muted">#<?php echo $address['id']; ?></small>
|
||||||
|
</div>
|
||||||
|
<p class="mb-1"><?php echo htmlspecialchars($address['address_line']); ?></p>
|
||||||
|
<small class="text-muted">کدپستی: <?php echo htmlspecialchars($address['postal_code']); ?></small>
|
||||||
|
<div class="mt-2">
|
||||||
|
<a href="#" class="btn btn-sm btn-outline-danger">حذف</a>
|
||||||
|
<a href="#" class="btn btn-sm btn-outline-primary">ویرایش</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
require_once 'includes/footer.php';
|
||||||
|
?>
|
||||||
76
shop.php
76
shop.php
@ -1,51 +1,57 @@
|
|||||||
<?php
|
<?php
|
||||||
|
$page_title = 'فروشگاه';
|
||||||
|
require_once 'includes/header.php';
|
||||||
require_once 'db/config.php';
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// Fetch all products from the database
|
||||||
try {
|
try {
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
$stmt = $pdo->query("SELECT * FROM products ORDER BY created_at DESC");
|
$stmt = $pdo->query("SELECT * FROM products ORDER BY created_at DESC");
|
||||||
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
error_log("DB Error: " . $e->getMessage());
|
error_log("Database error: " . $e->getMessage());
|
||||||
$products = [];
|
$products = [];
|
||||||
|
$db_error = "خطا در بارگذاری محصولات. لطفا بعدا تلاش کنید.";
|
||||||
}
|
}
|
||||||
|
|
||||||
$page_title = 'فروشگاه';
|
|
||||||
include 'includes/header.php';
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="text-center mb-5">
|
<main class="container py-5">
|
||||||
<h1 class="display-4 fw-bold">گالری محصولات</h1>
|
<div class="text-center mb-5" data-aos="fade-down">
|
||||||
<p class="lead text-muted">دستسازههایی از چرم طبیعی، با عشق و دقت</p>
|
<h1 class="display-4 fw-bold">مجموعه کامل محصولات</h1>
|
||||||
</div>
|
<p class="fs-5">دستسازههایی از چرم طبیعی، با عشق و دقت.</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($db_error)): ?>
|
||||||
<?php if (!empty($products)):
|
<div class="alert alert-danger">
|
||||||
$delay = 0;
|
<?php echo $db_error; ?>
|
||||||
foreach ($products as $product):
|
|
||||||
?>
|
|
||||||
<div class="col" data-aos="fade-up" data-aos-delay="<?php echo $delay; ?>">
|
|
||||||
<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
|
|
||||||
$delay += 100;
|
|
||||||
endforeach;
|
|
||||||
?>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="col-12">
|
|
||||||
<p class="text-center p-5 bg-light rounded-3">محصولی برای نمایش یافت نشد.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php include 'includes/footer.php'; ?>
|
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4 g-lg-5">
|
||||||
|
<?php
|
||||||
|
if (!empty($products)) {
|
||||||
|
$delay = 0;
|
||||||
|
foreach ($products as $product) {
|
||||||
|
echo '<div class="col" data-aos="fade-up" data-aos-delay="' . $delay . '">';
|
||||||
|
echo ' <div class="product-card h-100">';
|
||||||
|
echo ' <div class="product-image">';
|
||||||
|
echo ' <a href="product.php?id=' . htmlspecialchars($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=' . htmlspecialchars($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 = ($delay + 100) % 400; // Stagger animation delay
|
||||||
|
}
|
||||||
|
} else if (empty($db_error)) {
|
||||||
|
echo '<div class="col-12"><p class="text-center text-white-50 fs-4">در حال حاضر محصولی برای نمایش وجود ندارد.</p></div>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
75
verify.php
Normal file
75
verify.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Redirect if email is not in session (user hasn't come from login page)
|
||||||
|
if (!isset($_SESSION['otp_email'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$email_for_display = htmlspecialchars($_SESSION['otp_email']);
|
||||||
|
$page_title = "تایید کد یکبار مصرف";
|
||||||
|
?>
|
||||||
|
<!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 $page_title; ?> - آتیمه</title>
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
||||||
|
<!-- Vazirmatn Font -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet" type="text/css" />
|
||||||
|
<!-- Main Custom CSS (for variables) -->
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<!-- Custom Auth CSS -->
|
||||||
|
<link rel="stylesheet" href="assets/css/auth_style.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<div class="auth-bg">
|
||||||
|
<div class="auth-bg-content">
|
||||||
|
<h1>فقط یک قدم دیگر...</h1>
|
||||||
|
<p>کد تاییدی که به ایمیل شما ارسال شده را وارد کنید تا وارد دنیای شگفتانگیز چرم شوید.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="auth-form-wrapper">
|
||||||
|
<div class="auth-form-container">
|
||||||
|
<div class="form-header text-center">
|
||||||
|
<h2>تایید کد</h2>
|
||||||
|
<p>کد ۶ رقمی ارسال شده به <strong><?php echo $email_for_display; ?></strong> را وارد کنید.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if(isset($_SESSION['flash_message'])): ?>
|
||||||
|
<div class="alert alert-<?php echo $_SESSION['flash_message']['type']; ?> alert-dismissible fade show" role="alert">
|
||||||
|
<?php echo htmlspecialchars($_SESSION['flash_message']['message']); ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php unset($_SESSION['flash_message']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form action="auth_handler.php?action=verify_otp" method="POST">
|
||||||
|
<input type="hidden" name="email" value="<?php echo $email_for_display; ?>">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="otp_code" class="form-label visually-hidden">کد تایید</label>
|
||||||
|
<input type="text" class="form-control text-center" id="otp_code" name="otp_code" placeholder="- - - - - -" required pattern="\d{6}" maxlength="6">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-grid mt-4">
|
||||||
|
<button type="submit" class="btn btn-primary">تایید و ورود</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="auth-footer">
|
||||||
|
<p>ایمیل را اشتباه وارد کردید؟ <a href="login.php">بازگشت</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap JS -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user