diff --git a/admin/bookings.php b/admin/bookings.php new file mode 100644 index 0000000..97bba6e --- /dev/null +++ b/admin/bookings.php @@ -0,0 +1,100 @@ +prepare("UPDATE bookings SET status = ? WHERE id = ?"); + $stmt->execute([$status, $id]); + $success = 'Booking status updated to ' . $status; +} + +$bookings = db()->query(" + SELECT b.*, u.name as user_name, u.email as user_email, c.brand, c.model, c.price + FROM bookings b + JOIN users u ON b.user_id = u.id + JOIN cars c ON b.car_id = c.id + ORDER BY b.booking_date DESC +")->fetchAll(); +?> + + + + + + Bookings - AFG Admin + + + + +
+ + + +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
CustomerCar RequestedPriceDateStatusActions
+
+ +
$ + + + + + +
No bookings found.
+
+
+ + diff --git a/admin/cars.php b/admin/cars.php new file mode 100644 index 0000000..bc6cbf6 --- /dev/null +++ b/admin/cars.php @@ -0,0 +1,186 @@ +prepare("DELETE FROM cars WHERE id = ?"); + if ($stmt->execute([$id])) { + $success = 'Car deleted successfully.'; + } +} + +// Handle Add / Edit +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $action = $_POST['action'] ?? ''; + $brand = $_POST['brand'] ?? ''; + $model = $_POST['model'] ?? ''; + $year = $_POST['year'] ?? ''; + $price = $_POST['price'] ?? ''; + $city = $_POST['city'] ?? 'Kabul'; + $fuel = $_POST['fuel_type'] ?? 'Gasoline'; + $transmission = $_POST['transmission'] ?? 'Automatic'; + $description = $_POST['description'] ?? ''; + $id = $_POST['id'] ?? null; + + // Handle Image Upload + $main_image = $_POST['current_image'] ?? ''; + if (!empty($_FILES['image']['name'])) { + $target_dir = "uploads/"; + $file_ext = strtolower(pathinfo($_FILES["image"]["name"], PATHINFO_EXTENSION)); + $file_name = time() . "_" . uniqid() . "." . $file_ext; + $target_file = $target_dir . $file_name; + + if (move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)) { + $main_image = 'admin/' . $target_file; + } + } + + if ($action === 'add') { + $stmt = db()->prepare("INSERT INTO cars (brand, model, year, price, city, fuel_type, transmission, description, main_image) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + if ($stmt->execute([$brand, $model, $year, $price, $city, $fuel, $transmission, $description, $main_image])) { + $success = 'Car added successfully.'; + } + } elseif ($action === 'edit') { + $stmt = db()->prepare("UPDATE cars SET brand=?, model=?, year=?, price=?, city=?, fuel_type=?, transmission=?, description=?, main_image=? WHERE id=?"); + if ($stmt->execute([$brand, $model, $year, $price, $city, $fuel, $transmission, $description, $main_image, $id])) { + $success = 'Car updated successfully.'; + } + } +} + +$cars = db()->query("SELECT * FROM cars ORDER BY created_at DESC")->fetchAll(); +?> + + + + + + Cars Management - AFG Admin + + + + + +
+ + +
+

Inventory Overview

+ + + Add New Car + +
+ + +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ImageBrand & ModelYearPriceCityStatusActions
+ + +
+ +
$ + + + + +
+ + + +
+
+
+ + + +
+ + diff --git a/admin/css/style.css b/admin/css/style.css new file mode 100644 index 0000000..7b5cce9 --- /dev/null +++ b/admin/css/style.css @@ -0,0 +1,228 @@ +:root { + --primary: #1e3a8a; + --accent: #2563eb; + --bg: #f3f4f6; + --card: #ffffff; + --text: #1f2937; + --sidebar: #111827; + --sidebar-text: #9ca3af; + --danger: #dc2626; + --success: #16a34a; + --radius: 12px; +} + +[data-theme="dark"] { + --bg: #111827; + --card: #1f2937; + --text: #f3f4f6; + --sidebar: #0f172a; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Inter', sans-serif; +} + +body { + background: var(--bg); + color: var(--text); + display: flex; + transition: background 0.3s; +} + +/* Sidebar */ +.sidebar { + width: 260px; + height: 100vh; + background: var(--sidebar); + color: white; + position: fixed; + left: 0; + top: 0; + padding: 2rem 1rem; + display: flex; + flex-direction: column; + z-index: 1000; +} + +.sidebar-logo { + font-size: 1.5rem; + font-weight: bold; + margin-bottom: 2rem; + color: white; + text-decoration: none; + display: flex; + align-items: center; + gap: 10px; +} + +.nav-links { + list-style: none; + flex: 1; +} + +.nav-links li { + margin-bottom: 0.5rem; +} + +.nav-links a { + color: var(--sidebar-text); + text-decoration: none; + padding: 0.8rem 1rem; + display: flex; + align-items: center; + gap: 12px; + border-radius: var(--radius); + transition: 0.3s; +} + +.nav-links a:hover, .nav-links a.active { + background: var(--accent); + color: white; +} + +/* Main Content */ +.main-content { + margin-left: 260px; + width: calc(100% - 260px); + padding: 2rem; +} + +.top-bar { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +/* Dashboard Cards */ +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 1.5rem; + margin-bottom: 2rem; +} + +.stat-card { + background: var(--card); + padding: 1.5rem; + border-radius: var(--radius); + box-shadow: 0 4px 6px rgba(0,0,0,0.05); + display: flex; + align-items: center; + gap: 1rem; + transition: transform 0.3s; +} + +.stat-card:hover { + transform: translateY(-5px); +} + +.stat-icon { + width: 48px; + height: 48px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(37, 99, 235, 0.1); + color: var(--accent); +} + +.stat-info h3 { + font-size: 0.875rem; + color: #6b7280; + margin-bottom: 0.25rem; +} + +.stat-info p { + font-size: 1.5rem; + font-weight: bold; +} + +/* Data Table */ +.data-table-container { + background: var(--card); + border-radius: var(--radius); + padding: 1.5rem; + box-shadow: 0 4px 6px rgba(0,0,0,0.05); + overflow-x: auto; +} + +table { + width: 100%; + border-collapse: collapse; +} + +th { + text-align: left; + padding: 1rem; + border-bottom: 2px solid var(--bg); + color: #6b7280; +} + +td { + padding: 1rem; + border-bottom: 1px solid var(--bg); +} + +/* Buttons */ +.btn { + padding: 0.5rem 1rem; + border-radius: 8px; + border: none; + cursor: pointer; + font-weight: 500; + transition: 0.3s; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 8px; +} + +.btn-primary { background: var(--accent); color: white; } +.btn-danger { background: var(--danger); color: white; } +.btn-success { background: var(--success); color: white; } + +/* Modal (CSS Only) */ +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.5); + z-index: 2000; + justify-content: center; + align-items: center; +} + +.modal:target { + display: flex; +} + +.modal-content { + background: var(--card); + padding: 2rem; + border-radius: var(--radius); + width: 500px; + max-width: 90%; +} + +/* Mobile Responsive */ +@media (max-width: 768px) { + .sidebar { + width: 70px; + padding: 2rem 0.5rem; + } + .sidebar-logo span, .nav-links a span { + display: none; + } + .main-content { + margin-left: 70px; + width: calc(100% - 70px); + } +} diff --git a/admin/dashboard.php b/admin/dashboard.php new file mode 100644 index 0000000..d7518d1 --- /dev/null +++ b/admin/dashboard.php @@ -0,0 +1,121 @@ +query("SELECT COUNT(*) FROM cars")->fetchColumn(); +$total_users = db()->query("SELECT COUNT(*) FROM users")->fetchColumn(); +$total_bookings = db()->query("SELECT COUNT(*) FROM bookings")->fetchColumn(); +$total_messages = db()->query("SELECT COUNT(*) FROM contact_messages")->fetchColumn(); + +$recent_bookings = db()->query(" + SELECT b.*, u.name as user_name, c.brand, c.model + FROM bookings b + JOIN users u ON b.user_id = u.id + JOIN cars c ON b.car_id = c.id + ORDER BY b.booking_date DESC LIMIT 5 +")->fetchAll(); + +$recent_messages = db()->query("SELECT * FROM contact_messages ORDER BY created_at DESC LIMIT 5")->fetchAll(); +?> + + + + + + Dashboard - AFG Car Sales + + + + + +
+ + +
+
+
🚗
+
+

Total Cars

+

+
+
+
+
👥
+
+

Total Users

+

+
+
+
+
📅
+
+

Bookings

+

+
+
+
+
📩
+
+

Messages

+

+
+
+
+ +
+ +
+

Recent Bookings

+ + + + + + + + + + + + + + + + + + + + + + +
UserCarStatusDate
+ + + +
No recent bookings
+
+ + +
+

Recent Messages

+
+ +
+

+

+

+
+ + +

No recent messages

+ +
+
+
+
+ + diff --git a/admin/includes/auth.php b/admin/includes/auth.php new file mode 100644 index 0000000..df1b2a7 --- /dev/null +++ b/admin/includes/auth.php @@ -0,0 +1,20 @@ +prepare("SELECT * FROM users WHERE id = ?"); + $stmt->execute([$_SESSION['user_id']]); + return $stmt->fetch(); +} diff --git a/admin/includes/header.php b/admin/includes/header.php new file mode 100644 index 0000000..e4aca82 --- /dev/null +++ b/admin/includes/header.php @@ -0,0 +1,14 @@ +
+
+

+
+
+
+

+

Administrator

+
+
+ +
+
+
diff --git a/admin/includes/sidebar.php b/admin/includes/sidebar.php new file mode 100644 index 0000000..035482f --- /dev/null +++ b/admin/includes/sidebar.php @@ -0,0 +1,50 @@ + diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..a4c4295 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,102 @@ +prepare("SELECT * FROM users WHERE email = ? AND role = 'admin'"); + $stmt->execute([$email]); + $user = $stmt->fetch(); + + if ($user && password_verify($password, $user['password'])) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['user_name'] = $user['name']; + $_SESSION['user_role'] = $user['role']; + header('Location: dashboard.php'); + exit(); + } else { + $error = 'Invalid admin credentials.'; + } +} +?> + + + + + + Admin Login - Car Sales Afghanistan + + + + +
+

Admin Portal

+ +
+ +
+
+ + +
+
+ + +
+ +
+
+ + diff --git a/admin/logout.php b/admin/logout.php new file mode 100644 index 0000000..e88bacd --- /dev/null +++ b/admin/logout.php @@ -0,0 +1,5 @@ +prepare("DELETE FROM contact_messages WHERE id = ?"); + $stmt->execute([$id]); + $success = 'Message deleted.'; +} + +// Handle Read Status +if (isset($_GET['read'])) { + $id = $_GET['read']; + $stmt = db()->prepare("UPDATE contact_messages SET is_read = 1 WHERE id = ?"); + $stmt->execute([$id]); +} + +$messages = db()->query("SELECT * FROM contact_messages ORDER BY created_at DESC")->fetchAll(); +?> + + + + + + Messages - AFG Admin + + + + +
+ + + +
+ +
+ + +
+ +
+
+
+

+

From: ()

+
+
+ +
+ + Mark as Read + + Delete +
+
+
+
+ +
+
+ + +
+ No customer messages found. +
+ +
+
+ + diff --git a/admin/settings.php b/admin/settings.php new file mode 100644 index 0000000..5b3f7a3 --- /dev/null +++ b/admin/settings.php @@ -0,0 +1,126 @@ +query("SELECT * FROM settings WHERE id = 1")->fetch(); + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (isset($_POST['update_site'])) { + $site_name = $_POST['site_name']; + $primary_color = $_POST['primary_color']; + $dark_mode = isset($_POST['dark_mode']) ? 1 : 0; + + $stmt = db()->prepare("UPDATE settings SET site_name = ?, primary_color = ?, dark_mode = ? WHERE id = 1"); + if ($stmt->execute([$site_name, $primary_color, $dark_mode])) { + $success = 'Site settings updated successfully.'; + $settings = db()->query("SELECT * FROM settings WHERE id = 1")->fetch(); + } + } + + if (isset($_POST['update_password'])) { + $old_pass = $_POST['old_password']; + $new_pass = $_POST['new_password']; + $confirm_pass = $_POST['confirm_password']; + + $user = getAdminData(); + if (password_verify($old_pass, $user['password'])) { + if ($new_pass === $confirm_pass) { + if (strlen($new_pass) >= 8) { + $hashed = password_hash($new_pass, PASSWORD_DEFAULT); + $stmt = db()->prepare("UPDATE users SET password = ? WHERE id = ?"); + $stmt->execute([$hashed, $user['id']]); + $success = 'Admin password updated successfully.'; + } else { + $error = 'New password must be at least 8 characters.'; + } + } else { + $error = 'Passwords do not match.'; + } + } else { + $error = 'Incorrect old password.'; + } + } +} +?> + + + + + + Settings - AFG Admin + + + + +
+ + + +
+ +
+ + + +
+ +
+ + +
+ +
+

General Configuration

+
+ +
+ + +
+
+ + +
+
+ style="width: 20px; height: 20px;"> + +
+ +
+
+ + +
+

Admin Security

+
+ +
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+ + diff --git a/admin/users.php b/admin/users.php new file mode 100644 index 0000000..ffb32f7 --- /dev/null +++ b/admin/users.php @@ -0,0 +1,94 @@ +prepare("DELETE FROM users WHERE id = ?"); + $stmt->execute([$id]); + $success = 'User deleted successfully.'; + } +} + +// Handle Role Change +if (isset($_GET['toggle_role'])) { + $id = $_GET['toggle_role']; + $current_role = $_GET['current']; + $new_role = ($current_role === 'admin') ? 'user' : 'admin'; + + $stmt = db()->prepare("UPDATE users SET role = ? WHERE id = ?"); + $stmt->execute([$new_role, $id]); + $success = 'User role updated.'; +} + +$users = db()->query("SELECT * FROM users ORDER BY created_at DESC")->fetchAll(); +?> + + + + + + Users - AFG Admin + + + + +
+ + + +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + +
IDNameEmailRoleJoined DateActions
# + + + + +
+ + + + + + + + +
+
+
+
+ + diff --git a/db/database.sql b/db/database.sql index b1cb96e..2cf80d6 100644 --- a/db/database.sql +++ b/db/database.sql @@ -16,18 +16,35 @@ CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS cars ( id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT, brand VARCHAR(50) NOT NULL, model VARCHAR(50) NOT NULL, year INT NOT NULL, price DECIMAL(10, 2) NOT NULL, + fuel_type VARCHAR(20) DEFAULT 'Gasoline', + transmission VARCHAR(20) DEFAULT 'Automatic', city ENUM('Kabul', 'Herat', 'Mazar-i-Sharif', 'Kandahar', 'Jalalabad') NOT NULL, description TEXT, main_image VARCHAR(255), status ENUM('available', 'sold') DEFAULT 'available', is_hot_deal BOOLEAN DEFAULT FALSE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS car_images ( + id INT AUTO_INCREMENT PRIMARY KEY, + car_id INT, + image_path VARCHAR(255) NOT NULL, + FOREIGN KEY (car_id) REFERENCES cars(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS bookings ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT, + car_id INT, + status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending', + booking_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (car_id) REFERENCES cars(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS contact_messages ( @@ -36,9 +53,20 @@ CREATE TABLE IF NOT EXISTS contact_messages ( email VARCHAR(100) NOT NULL, subject VARCHAR(200), message TEXT NOT NULL, + is_read BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); +CREATE TABLE IF NOT EXISTS settings ( + id INT PRIMARY KEY AUTO_INCREMENT, + site_name VARCHAR(100) DEFAULT 'Car Sales Afghanistan', + site_logo VARCHAR(255) DEFAULT 'assets/images/logo.png', + primary_color VARCHAR(10) DEFAULT '#1e3a8a', + dark_mode BOOLEAN DEFAULT TRUE +); + +INSERT IGNORE INTO settings (id, site_name) VALUES (1, 'Car Sales Afghanistan'); + -- Default Admin Account (Password: 12345678) INSERT IGNORE INTO users (id, name, email, password, role) VALUES (1, 'Mohammad Sadiq', 'admin@gmail.com', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'admin'); diff --git a/includes/header.php b/includes/header.php index 99c4fb2..8be62b6 100644 --- a/includes/header.php +++ b/includes/header.php @@ -22,8 +22,14 @@
  • Contact
  • - Login - Register + + Hi, + Dashboard + Logout + + Login + Register +
    diff --git a/login.php b/login.php index e56593c..ba4ea8a 100644 --- a/login.php +++ b/login.php @@ -21,7 +21,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $_SESSION['user_name'] = $user['name']; $_SESSION['user_role'] = $user['role']; - header('Location: index.php'); + if ($user['role'] === 'admin') { + header('Location: admin/dashboard.php'); + } else { + header('Location: user_dashboard.php'); + } exit; } else { $error = 'Invalid email or password.'; diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..95db42c --- /dev/null +++ b/logout.php @@ -0,0 +1,6 @@ +prepare(" + SELECT b.*, c.brand, c.model, c.year, c.price, c.main_image + FROM bookings b + JOIN cars c ON b.car_id = c.id + WHERE b.user_id = ? + ORDER BY b.booking_date DESC + "); + $stmt->execute([$user_id]); + $bookings = $stmt->fetchAll(); +} catch (PDOException $e) { + $bookings = []; +} + +include 'includes/header.php'; +?> + +
    +
    + +
    +
    +
    +
    + +
    +

    +

    Premium Member

    +
    +
    + +
    +
    + + +
    +
    +

    Welcome to your Dashboard

    +

    Manage your bookings and view your saved cars.

    +
    + +
    +

    My Booking Requests

    + + +
    +

    You haven't made any booking requests yet.

    + Browse Available Cars +
    + +
    + + + + + + + + + + + + + + + + + + + +
    CarPriceDateStatus
    +
    + Car +
    + +
    +
    +
    +
    $ + + + + +
    +
    + +
    +
    +
    +
    + + + +