diff --git a/.gemini/tmp/sales_chart_cache.json b/.gemini/tmp/sales_chart_cache.json new file mode 100644 index 00000000..336a6388 --- /dev/null +++ b/.gemini/tmp/sales_chart_cache.json @@ -0,0 +1 @@ +{"labels":["1404-09"],"data":[2940000]} \ No newline at end of file diff --git a/.gitignore b/.gitignore index e427ff3c..399e2ae8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ node_modules/ */node_modules/ */build/ + +# Ignore environment files +.env diff --git a/about.php b/about.php index aff5f294..1ff43558 100644 --- a/about.php +++ b/about.php @@ -3,59 +3,58 @@ $page_title = 'درباره ما'; require_once 'includes/header.php'; ?> -
-
-

داستان آتیمه

+
+
+

داستان آتیمه

تلفیق هنر سنتی و طراحی مدرن

-
-
-
-
-
- هنر چرم‌دوزی -
-
-
-

باور ما

-

ما در آتیمه، به قدرت دست‌ها و اصالت مواد اولیه باور داریم. داستان ما از یک کارگاه کوچک و عشقی عمیق به هنر چرم‌دوزی آغاز شد. هدف ما خلق آثاری است که نه تنها یک وسیله کاربردی، بلکه بخشی از داستان و استایل روزمره شما باشند؛ آثاری که با گذر زمان، زیباتر و شخصی‌تر می‌شوند.

-

هر محصول، حاصل ساعت‌ها کار دست هنرمندان ماهر و استفاده از بهترین و باکیفیت‌ترین چرم‌های طبیعی است. ما به جزئیات اهمیت می‌دهیم، از انتخاب نخ گرفته تا طراحی هر برش و دوخت. این تعهد به کیفیت، تضمین می‌کند که هر ساخته‌ دست ما، اثری ماندگار و بی‌همتا باشد.

- مشاهده مجموعه ما -
-
+
+
+
+ هنر چرم‌دوزی +
+
+
+

باور ما

+

ما در آتیمه، به قدرت دست‌ها و اصالت مواد اولیه باور داریم. داستان ما از یک کارگاه کوچک و عشقی عمیق به هنر چرم‌دوزی آغاز شد. هدف ما خلق آثاری است که نه تنها یک وسیله کاربردی، بلکه بخشی از داستان و استایل روزمره شما باشند؛ آثاری که با گذر زمان، زیباتر و شخصی‌تر می‌شوند.

+

هر محصول، حاصل ساعت‌ها کار دست هنرمندان ماهر و استفاده از بهترین و باکیفیت‌ترین چرم‌های طبیعی است. ما به جزئیات اهمیت می‌دهیم، از انتخاب نخ گرفته تا طراحی هر برش و دوخت. این تعهد به کیفیت، تضمین می‌کند که هر ساخته‌ دست ما، اثری ماندگار و بی‌همتا باشد.

+ مجموعه ما را ببینید
-
-
+
+
+

ارزش‌های ما

+
+
-
- +
+

تعهد به کیفیت

-

استفاده از بهترین مواد اولیه و کنترل کیفی دقیق در تمام مراحل تولید.

+

استفاده از بهترین مواد اولیه و کنترل کیفی دقیق در تمام مراحل تولید.

-
- +
+

هنر دست

-

تمام محصولات ما با عشق و دقت توسط هنرمندان ماهر ساخته می‌شوند.

+

تمام محصولات ما با عشق و دقت توسط هنرمندان ماهر ساخته می‌شوند.

-
- +
+

طراحی ماندگار

-

خلق آثاری مدرن و در عین حال کلاسیک که هیچ‌گاه از مد نمی‌افتند.

+

خلق آثاری مدرن و در عین حال کلاسیک که هیچ‌گاه از مد نمی‌افتند.

-
+ diff --git a/admin/add_product.php b/admin/add_product.php index 24c01468..f4c488fd 100644 --- a/admin/add_product.php +++ b/admin/add_product.php @@ -1,8 +1,6 @@ -
-
-
-

افزودن محصول جدید

- انصراف -
+ -
-
-
-
- - -
-
- - -
-
-
- - -
-
- - -
-
-
- - -
کدهای رنگ هگزادسیمال را با کاما جدا کنید.
-
-
- - -
-
- -
-
+
+

افزودن محصول جدید

+ بازگشت +
+ +
+
+
+
+ +
-
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + + کدهای رنگ هگزادسیمال را با کاما جدا کنید. +
+ +
+ +
+ +
+ +
+
- - + \ No newline at end of file diff --git a/admin/api.php b/admin/api.php new file mode 100644 index 00000000..f6e2f0e8 --- /dev/null +++ b/admin/api.php @@ -0,0 +1,136 @@ + 'Unauthorized']); + exit; +} + +// IMPORTANT: Close the session immediately after use to prevent locking. +// This allows other concurrent requests from the same user to be processed. +session_write_close(); + +$action = $_GET['action'] ?? ''; +$pdo = db(); + +if ($action === 'get_sales_data') { + require_once __DIR__ . '/../includes/jdf.php'; + + $cache_file = __DIR__ . '/cache/sales_chart.json'; + $cache_lifetime = 3600; // 1 hour + + // Clear PHP's stat cache to ensure we get the most up-to-date file status + clearstatcache(); + + if (file_exists($cache_file) && is_readable($cache_file) && (time() - filemtime($cache_file) < $cache_lifetime)) { + $cached_data = file_get_contents($cache_file); + // Verify that the cache content is a valid JSON + if ($cached_data && json_decode($cached_data) !== null) { + header('X-Cache: HIT'); + echo $cached_data; + exit; + } + } + + // CACHE MISS: Regenerate the data + try { + $stmt = $pdo->prepare(" + SELECT + YEAR(created_at) as year, + MONTH(created_at) as month, + SUM(total_amount) as total_sales + FROM orders + WHERE status = 'Delivered' + GROUP BY year, month + ORDER BY year ASC, month ASC + "); + $stmt->execute(); + $sales_data = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $labels = []; + $data = []; + foreach ($sales_data as $row) { + $jalali_date = gregorian_to_jalali($row['year'], $row['month'], 1); + $labels[] = $jalali_date[0] . '-' . str_pad($jalali_date[1], 2, '0', STR_PAD_LEFT); + $data[] = (float)$row['total_sales']; + } + + $response_data = json_encode(['labels' => $labels, 'data' => $data]); + + // Atomic Write Operation + $cache_dir = dirname($cache_file); + if (!is_dir($cache_dir)) { + mkdir($cache_dir, 0755, true); + } + $temp_file = $cache_file . '.' . uniqid() . '.tmp'; + if (file_put_contents($temp_file, $response_data) !== false) { + // If rename fails, the old (possibly stale) cache will be used, which is acceptable. + // The temp file will be cleaned up on subsequent runs or by a cron job. + rename($temp_file, $cache_file); + } + + header('X-Cache: MISS'); + echo $response_data; + + } catch (PDOException $e) { + http_response_code(500); + error_log("FATAL: DB Exception during sales data generation: " . $e->getMessage()); + echo json_encode(['error' => 'Database error while fetching sales data.']); + } + exit; +} + +if ($action === 'get_stats') { + try { + // Optimized: Fetch all stats in a single query + $query = " + SELECT + (SELECT SUM(total_amount) FROM orders WHERE status = 'Delivered') as total_sales, + (SELECT COUNT(*) FROM orders WHERE status = 'Shipped') as shipped_orders, + (SELECT COUNT(*) FROM orders WHERE status = 'Cancelled') as cancelled_orders, + (SELECT COUNT(*) FROM orders WHERE status = 'Processing') as processing_orders, + (SELECT COUNT(*) FROM users) as total_users, + (SELECT COUNT(*) FROM page_views) as total_views, + (SELECT COUNT(*) FROM page_views WHERE YEAR(view_timestamp) = YEAR(CURDATE()) AND MONTH(view_timestamp) = MONTH(CURDATE())) as this_month_views, + (SELECT COUNT(*) FROM page_views WHERE YEAR(view_timestamp) = YEAR(CURDATE() - INTERVAL 1 MONTH) AND MONTH(view_timestamp) = MONTH(CURDATE() - INTERVAL 1 MONTH)) as last_month_views + "; + + $stmt = $pdo->query($query); + $stats = $stmt->fetch(PDO::FETCH_ASSOC); + + $this_month_views = (int)($stats['this_month_views'] ?? 0); + $last_month_views = (int)($stats['last_month_views'] ?? 0); + + $percentage_change = 0; + if ($last_month_views > 0) { + $percentage_change = (($this_month_views - $last_month_views) / $last_month_views) * 100; + } elseif ($this_month_views > 0) { + $percentage_change = 100; + } + + echo json_encode([ + 'total_sales' => (float)($stats['total_sales'] ?? 0), + 'shipped_orders' => (int)($stats['shipped_orders'] ?? 0), + 'cancelled_orders' => (int)($stats['cancelled_orders'] ?? 0), + 'processing_orders' => (int)($stats['processing_orders'] ?? 0), + 'total_users' => (int)($stats['total_users'] ?? 0), + 'total_page_views' => [ + 'count' => (int)($stats['total_views'] ?? 0), + 'percentage_change' => round($percentage_change, 2) + ], + ]); + + } catch (PDOException $e) { + http_response_code(500); + error_log("API Error (get_stats): " . $e->getMessage()); + echo json_encode(['error' => 'Database error while fetching stats.']); + } + exit; +} + +http_response_code(400); +echo json_encode(['error' => 'Invalid action']); diff --git a/admin/assets/css/admin_style.css b/admin/assets/css/admin_style.css index 225def67..8121f4ee 100644 --- a/admin/assets/css/admin_style.css +++ b/admin/assets/css/admin_style.css @@ -1,259 +1,348 @@ - -/* ================================================================= - ADMIN PANEL MODERN STYLES - ================================================================= */ +@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;600;700&display=swap'); +@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css'); :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; + --admin-bg: #111214; + --admin-surface: #1a1b1e; + --admin-text: #eceff1; + --admin-text-muted: #90a4ae; + --admin-primary: #c09f80; /* Soft gold from luxury theme */ + --admin-border: #37474f; + --admin-success: #4caf50; + --admin-danger: #f44336; + --admin-warning: #ff9800; + --admin-info: #2196f3; } -/* --- General Body & Typography --- */ -body.admin-page { +body.admin-dark-theme { background-color: var(--admin-bg); - color: var(--admin-text-primary); - font-family: var(--admin-font); - padding-right: 0; /* Reset previous style */ + color: var(--admin-text); + font-family: 'Vazirmatn', sans-serif; + line-height: 1.6; + margin: 0; + padding: 0; + display: flex; + min-height: 100vh; } -.admin-main-content { - padding: 2rem; - margin-right: 280px; /* Space for the new sidebar */ - transition: margin-right 0.3s ease; +.admin-wrapper { + display: flex; + width: 100%; } -h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { - color: var(--admin-text-primary); +/* --- Sidebar / Navigation --- */ +.admin-sidebar { + width: 260px; + background-color: var(--admin-surface); + border-right: 1px solid var(--admin-border); + padding: 1.5rem 0; + display: flex; + flex-direction: column; + transition: width 0.3s ease; +} + +.sidebar-header { + padding: 0 1.5rem 1.5rem 1.5rem; + text-align: center; + border-bottom: 1px solid var(--admin-border); +} + +.sidebar-header h2 a { + color: var(--admin-text); + text-decoration: none; + font-size: 1.5rem; font-weight: 700; } -a { - color: var(--admin-accent); -} -a:hover { - color: var(--admin-accent-hover); +.sidebar-header h2 a span { + color: var(--admin-primary); } -/* --- 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 { +.admin-nav { flex-grow: 1; - padding-top: 1rem; + list-style: none; + padding: 1.5rem 0 0 0; + margin: 0; } -.admin-sidebar .nav-link { - color: var(--admin-text-secondary); + +.admin-nav-item { + margin: 0; +} + +.admin-nav-link { display: flex; align-items: center; - font-size: 1rem; - font-weight: 500; + gap: 0.8rem; 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); + color: var(--admin-text-muted); + text-decoration: none; font-weight: 600; -} -.admin-sidebar .nav-link .bi { - font-size: 1.2rem; - margin-left: 0.75rem; /* For RTL, it should be margin-left */ + font-size: 0.95rem; + border-left: 4px solid transparent; + transition: all 0.3s ease; } -.admin-sidebar-footer { - padding: 1rem 1.5rem; +.admin-nav-link i { + font-size: 1.1rem; + width: 20px; + text-align: center; +} + +.admin-nav-link:hover { + background-color: var(--admin-bg); + color: var(--admin-primary); + border-left-color: var(--admin-primary); +} + +.admin-nav-link.active { + background-color: var(--admin-bg); + color: var(--admin-text); + border-left-color: var(--admin-primary); + font-weight: 700; +} + +.sidebar-footer { + padding: 1.5rem; + text-align: center; border-top: 1px solid var(--admin-border); } -/* --- Dashboard Stat Cards --- */ -.stat-card { +.sidebar-footer a { + color: var(--admin-text-muted); + text-decoration: none; + font-size: 0.9rem; +} +.sidebar-footer a:hover { + color: var(--admin-primary); +} + +/* --- Main Content --- */ +.admin-main-content { + flex-grow: 1; + padding: 2rem; + overflow-y: auto; +} + +.admin-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +.admin-header h1 { + margin: 0; + font-size: 2rem; + font-weight: 700; +} + +/* --- General Components --- */ +.card { background-color: var(--admin-surface); border: 1px solid var(--admin-border); - border-radius: 0.75rem; + border-radius: 12px; + margin-bottom: 2rem; +} + +.card-header { + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--admin-border); + font-size: 1.1rem; + font-weight: 600; +} + +.card-body { + padding: 1.5rem; +} + +.table { + width: 100%; + border-collapse: collapse; +} + +.table th, .table td { + padding: 1rem; + text-align: right; + border-bottom: 1px solid var(--admin-border); +} + +.table th { + font-weight: 700; + color: var(--admin-text-muted); + font-size: 0.9rem; + text-transform: uppercase; +} + +.table tbody tr:last-child td { + border-bottom: none; +} + +.table tbody tr:hover { + background-color: var(--admin-bg); +} + +.btn { + padding: 0.6rem 1.2rem; + border-radius: 8px; + text-decoration: none; + font-weight: 600; + transition: all 0.3s ease; + border: none; + cursor: pointer; +} + +.btn-primary { + background-color: var(--admin-primary); + color: var(--admin-bg); +} +.btn-primary:hover { + opacity: 0.9; +} + +.btn-danger { + background-color: var(--admin-danger); + color: var(--admin-text); +} + +/* --- Stat Cards (from dashboard) --- */ +.stat-card { + background-color: var(--admin-surface); + border-radius: 12px; padding: 1.5rem; display: flex; align-items: center; - transition: all 0.3s ease; + gap: 1.5rem; + border: 1px solid var(--admin-border); + transition: transform 0.3s ease, box-shadow 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); + box-shadow: 0 5px 15px rgba(0,0,0,0.2); } -.stat-card .icon-container { + +.stat-card .icon { font-size: 2rem; - color: var(--admin-accent); - background-color: #363e4d; - width: 60px; - height: 60px; + padding: 1rem; 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; + color: var(--admin-text); } + .stat-card .stat-info p { margin: 0; - color: var(--admin-text-secondary); + font-size: 0.9rem; + color: var(--admin-text-muted); } -/* --- Dashboard Tables & Badges --- */ -.card-table .card-header { - background-color: transparent; - border-bottom: 1px solid var(--admin-border); - padding: 1rem 1.5rem; - font-weight: 600; +.stat-card .stat-info h3 { + margin: 0; + font-size: 2rem; + font-weight: 700; } -.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 */ +.icon.bg-success { background-color: var(--admin-success); } +.icon.bg-danger { background-color: var(--admin-danger); } +.icon.bg-warning { background-color: var(--admin-warning); } +.icon.bg-info { background-color: var(--admin-info); } +.icon.bg-primary { background-color: var(--admin-primary); } -/* --- Modal Styling --- */ -.modal-content { + +/* --- Chart Container --- */ +.chart-container { background-color: var(--admin-surface); - color: var(--admin-text-primary); + padding: 2rem; + border-radius: 12px; 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%); + +.chart-container h5 { + font-weight: 700; + margin-bottom: 1.5rem; + font-size: 1.2rem; } +/* --- Form styles --- */ +.form-group { + margin-bottom: 1.5rem; +} +.form-label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + color: var(--admin-text-muted); +} + +.form-control { + width: 100%; + padding: 0.8rem 1rem; + background-color: var(--admin-bg); + border: 1px solid var(--admin-border); + color: var(--admin-text); + border-radius: 8px; + box-sizing: border-box; +} + +.form-control:focus { + outline: none; + border-color: var(--admin-primary); + box-shadow: 0 0 0 2px rgba(192, 159, 128, 0.2); +} + +textarea.form-control { + min-height: 120px; + resize: vertical; +} + +/* Responsive */ @media (max-width: 992px) { - .admin-main-content { - margin-right: 0; + .admin-sidebar { + width: 70px; + } + .sidebar-header h2 { + display: none; + } + .admin-nav-link { + justify-content: center; + } + .admin-nav-link span { + display: none; + } +} +@media (max-width: 768px) { + .admin-wrapper { + flex-direction: column; } .admin-sidebar { - transform: translateX(280px); /* For RTL */ - transition: transform 0.3s ease; + width: 100%; + height: auto; + border-right: none; + border-bottom: 1px solid var(--admin-border); + flex-direction: row; + align-items: center; + padding: 0; } - .admin-sidebar.is-open { - transform: translateX(0); + .sidebar-header { + display: none; } - /* 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; + .admin-nav { + display: flex; + justify-content: space-around; + flex-grow: 1; + padding: 0; } -} + .admin-nav-link { + border-left: none; + border-bottom: 4px solid transparent; + } + .admin-nav-link:hover, .admin-nav-link.active { + border-left-color: transparent; + border-bottom-color: var(--admin-primary); + } + .sidebar-footer { + display: none; + } +} \ No newline at end of file diff --git a/admin/assets/css/dashboard_style.css b/admin/assets/css/dashboard_style.css new file mode 100644 index 00000000..54af29cb --- /dev/null +++ b/admin/assets/css/dashboard_style.css @@ -0,0 +1,69 @@ + +:root { + --dark-bg: #1a1a2e; + --dark-surface: #16213e; + --dark-primary: #0f3460; + --dark-secondary: #e94560; + --dark-text-primary: #ffffff; + --dark-text-secondary: #c5c5c5; +} + +body.dark-theme { + background-color: var(--dark-bg); + color: var(--dark-text-primary); +} + +.dashboard-container { + padding: 2rem; +} + +.stat-card-v2 { + background-color: var(--dark-surface); + border-radius: 12px; + padding: 1.5rem; + display: flex; + align-items: center; + gap: 1.5rem; + border: 1px solid var(--dark-primary); + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.stat-card-v2:hover { + transform: translateY(-5px); + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); +} + +.stat-card-v2 .icon-container { + font-size: 2.5rem; + padding: 1rem; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--dark-primary); +} + +.stat-card-v2 .stat-info p { + margin: 0; + font-size: 0.9rem; + color: var(--dark-text-secondary); +} + +.stat-card-v2 .stat-info h3 { + margin: 0; + font-size: 2rem; + font-weight: bold; +} + +.chart-container { + background-color: var(--dark-surface); + padding: 2rem; + border-radius: 12px; + border: 1px solid var(--dark-primary); + margin-top: 2rem; +} + +.chart-container h5 { + font-weight: bold; + margin-bottom: 1.5rem; +} diff --git a/admin/auth_check.php b/admin/auth_check.php index 6beff4c4..967b93b3 100644 --- a/admin/auth_check.php +++ b/admin/auth_check.php @@ -1,10 +1,8 @@ + +
+

گزارش‌ها

+
+ +
+ + + +
+
+
+
+

مجموع فروش (تحویل شده)

+

...

+
+
+
+
+
+

مجموع کاربران

+

...

+
+
+
+
+
+

سفارشات در حال ارسال

+

...

+
+
+
+
+
+

سفارشات لغو شده

+

...

+
+
+
+
+
+

سفارشات در حال پردازش

+

...

+
+
+
+
+
+

کل بازدید صفحات

+

...

+
+
+
+ +
+
نمودار فروش ماهانه (سفارشات تحویل شده)
+ +
+ + + + \ No newline at end of file diff --git a/admin/edit_product.php b/admin/edit_product.php index 72eea903..2ec38f72 100644 --- a/admin/edit_product.php +++ b/admin/edit_product.php @@ -3,9 +3,7 @@ session_start(); require_once __DIR__ . '/auth_check.php'; require_once __DIR__ . '/../db/config.php'; -// Sanitize and validate product ID $product_id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); - if (!$product_id) { $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'شناسه محصول نامعتبر است.']; header('Location: products.php'); @@ -17,116 +15,113 @@ try { $stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?"); $stmt->execute([$product_id]); $product = $stmt->fetch(PDO::FETCH_ASSOC); - if (!$product) { $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'محصول مورد نظر یافت نشد.']; header('Location: products.php'); exit; } } catch (PDOException $e) { - error_log("Database Error: " . $e->getMessage()); - $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'خطا در اتصال به پایگاه داده. لطفاً بعداً تلاش کنید.']; + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'خطا در اتصال به پایگاه داده.']; header('Location: products.php'); exit; } -$page_title = "ویرایش محصول: " . htmlspecialchars($product['name']); -require_once 'header.php'; +require_once __DIR__ . '/header.php'; ?> -