This commit is contained in:
Flatlogic Bot 2026-01-21 17:27:41 +00:00
parent 9c65e16259
commit 73e14b3353
15 changed files with 1212 additions and 461 deletions

View File

@ -1,29 +1,61 @@
# Car Sells in Afghanistan - Professional Car Dealership Platform
# Car Sells in Afghanistan
Welcome to the **Car Sells in Afghanistan** web application. This platform is a modern, high-performance solution for car dealerships in Afghanistan.
A modern, responsive car marketplace web application.
## 🚀 Key Features
## Features
- **Modern UI/UX:** Built with a "Mobile-First" approach using Bootstrap 5 and modern design principles.
- **Afghanistan-Specific Listings:** Includes detailed information such as Province, City, and Plate details.
- **Secure Authentication:** Integrated user registration and login system.
- **Advanced Admin Dashboard:** Full control over Users, Cars, Bookings, and Reviews.
* **Car Listings:** Browse approved cars with filters (Make, Province, Price).
* **Sell Your Car:** Users can submit cars for sale (requires Admin approval).
* **Buying System:** Secure "Buy Now" flow with Bank Details capture.
* **Receipts:** Auto-generated printable receipts for sold cars.
* **Admin Dashboard:**
* Analytics (Revenue, Sales, Inventory).
* Approve/Reject Car Requests.
* Manage Users and Inventory.
* **Responsive Design:** Built with Bootstrap 5.
## 🛠️ Step-by-Step Installation
## Local Setup Instructions (XAMPP/LAMP)
1. **Database Setup:** Create a MySQL database and update `db/config.php`.
2. **Initialize:** Run `db/setup_users.php`, `db/setup_cars.php`, and `db/migrate.php` in your browser or CLI.
1. **Clone/Copy** the project files to your web server root (e.g., `htdocs` or `/var/www/html`).
2. **Database Setup:**
* Create a MySQL database named `car_dealership`.
* Import the database structure. You can run the `setup_project.php` script if available, or manually import `db/migrations/*.sql`.
* **Quick Setup:** Access `http://localhost/setup_project.php` in your browser (if deployed).
3. **Configuration:**
* Edit `db/config.php` if your DB credentials differ from the defaults (User: `root`, Pass: empty).
4. **Run the App:**
* Open `http://localhost/` in your browser.
## 🔐 Admin Credentials
## Credentials
To access the admin dashboard, go to the login page and use:
* **Admin User:**
* Username: `admin`
* Password: `123` (or `12345678` if updated manually)
- **Login (Email or Username):** `admin @gmail.com`
- **Password:** `123`
## Usage Guide
*Note: We have updated the system to allow login using the exact format you requested.*
### Selling a Car
1. Register/Login.
2. Click "Sell Your Car" in the navigation.
3. Fill out the form and upload an image.
4. Status will be "Pending" until approved by Admin.
---
### Buying a Car
1. Login.
2. Click on a car (must be "Approved").
3. Click "Buy Now".
4. Enter Bank Province and Account Number.
5. Confirm. You will be redirected to the Receipt.
**Site Name:** Car Sells in Afghanistan
**Version:** 2.0 (Modern Edition)
### Admin Panel
1. Login as Admin.
2. Go to "Admin Panel" (dropdown menu).
3. **Sales Requests:** Approve newly submitted cars here.
4. **Sales History:** View all sold cars and revenue.
## Screenshots
*(Placeholders)*
* [Home Page]
* [Admin Dashboard]
* [Receipt]

View File

@ -10,50 +10,21 @@ require_once '../db/config.php';
$pdo = db();
// Handle booking status change
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$bookingId = filter_input(INPUT_POST, 'booking_id', FILTER_VALIDATE_INT);
$carId = filter_input(INPUT_POST, 'car_id', FILTER_VALIDATE_INT);
if ($bookingId && $carId) {
$pdo->beginTransaction();
try {
if (isset($_POST['approve'])) {
// Set booking to approved and car to sold
$pdo->prepare("UPDATE bookings SET status = 'approved' WHERE id = ?")->execute([$bookingId]);
$pdo->prepare("UPDATE cars SET status = 'sold' WHERE id = ?")->execute([$carId]);
} elseif (isset($_POST['cancel'])) {
// Set booking to cancelled and car back to for sale (approved)
$pdo->prepare("UPDATE bookings SET status = 'cancelled' WHERE id = ?")->execute([$bookingId]);
$pdo->prepare("UPDATE cars SET status = 'approved' WHERE id = ?")->execute([$carId]);
}
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
error_log("Booking status update failed: " . $e->getMessage());
}
}
header("Location: bookings.php");
exit();
}
// Fetch bookings with user and car details
// Removed email from selection
// Fetch sales with user and car details
$bookings = $pdo->query("
SELECT b.id, b.status, b.booking_date, u.username, c.make, c.model, c.id as car_id
SELECT b.*, u.username, c.make, c.model, c.year
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(PDO::FETCH_ASSOC);
$projectName = 'Manage Bookings';
$projectName = 'Sales History';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($projectName) ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
@ -63,62 +34,50 @@ $projectName = 'Manage Bookings';
<div class="admin-wrapper">
<?php include 'partials/sidebar.php'; ?>
<main class="admin-main-content">
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Manage Bookings</h1>
</div>
<h1 class="h2 mb-4">Sales History</h1>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Customer</th>
<th>Car</th>
<th>Booking Date</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($bookings)): ?>
<tr><td colspan="5" class="text-center">No bookings found.</td></tr>
<?php endif; ?>
<?php foreach ($bookings as $booking): ?>
<tr>
<td>
<div><b><?= htmlspecialchars($booking['username']) ?></b></div>
</td>
<td><?= htmlspecialchars($booking['make'] . ' ' . $booking['model']) ?></td>
<td><?= date("M d, Y, g:i A", strtotime($booking['booking_date'])) ?></td>
<td>
<span class="badge rounded-pill bg-<?= str_replace(['approved', 'pending', 'cancelled'], ['success', 'warning', 'danger'], $booking['status']) ?>">
<?= htmlspecialchars(ucfirst($booking['status'])) ?>
</span>
</td>
<td>
<?php if ($booking['status'] === 'pending'): ?>
<form method="POST" class="d-inline-flex gap-2" onsubmit="return confirm('Are you sure?');">
<input type="hidden" name="booking_id" value="<?= $booking['id'] ?>">
<input type="hidden" name="car_id" value="<?= $booking['car_id'] ?>">
<button type="submit" name="approve" class="btn btn-sm btn-success"><i class="bi bi-check-circle me-1"></i>Approve</button>
<button type="submit" name="cancel" class="btn btn-sm btn-danger"><i class="bi bi-x-circle me-1"></i>Cancel</button>
</form>
<?php else: ?>
<span class="text-muted">No actions</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Date</th>
<th>Customer</th>
<th>Car Details</th>
<th>Price</th>
<th>Bank Details</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php if (empty($bookings)): ?>
<tr><td colspan="6" class="text-center">No sales records found.</td></tr>
<?php endif; ?>
<?php foreach ($bookings as $sale): ?>
<tr>
<td><?= date("M d, Y", strtotime($sale['booking_date'])) ?></td>
<td>
<div class="fw-bold"><?= htmlspecialchars($sale['username']) ?></div>
</td>
<td><?= htmlspecialchars($sale['year'] . ' ' . $sale['make'] . ' ' . $sale['model']) ?></td>
<td class="fw-bold text-success">$<?= number_format($sale['sale_price'] ?? 0, 2) ?></td>
<td>
<small class="d-block text-muted">Prov: <?= htmlspecialchars($sale['bank_province'] ?? 'N/A') ?></small>
<small class="d-block text-muted">Acc: <?= htmlspecialchars($sale['bank_account_number'] ?? 'N/A') ?></small>
</td>
<td>
<span class="badge rounded-pill bg-success">SOLD</span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -14,26 +14,16 @@ $pdo = db();
$stats = [
'users' => $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn(),
'cars' => $pdo->query("SELECT COUNT(*) FROM cars")->fetchColumn(),
'bookings' => $pdo->query("SELECT COUNT(*) FROM bookings WHERE status = 'approved'")->fetchColumn(),
'pending_bookings' => $pdo->query("SELECT COUNT(*) FROM bookings WHERE status = 'pending'")->fetchColumn(),
'sales_count' => $pdo->query("SELECT COUNT(*) FROM bookings WHERE status = 'approved'")->fetchColumn(),
'revenue' => $pdo->query("SELECT SUM(sale_price) FROM bookings WHERE status = 'approved'")->fetchColumn(),
'pending_requests' => $pdo->query("SELECT COUNT(*) FROM cars WHERE status = 'pending'")->fetchColumn(),
];
// Chart Data: Sales over the last 30 days
$sales_data = $pdo->query("SELECT DATE(booking_date) as date, COUNT(*) as count FROM bookings WHERE status = 'approved' AND booking_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY DATE(booking_date) ORDER BY date ASC")->fetchAll(PDO::FETCH_ASSOC);
$sales_data = $pdo->query("SELECT DATE(booking_date) as date, COUNT(*) as count, SUM(sale_price) as total FROM bookings WHERE status = 'approved' AND booking_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY DATE(booking_date) ORDER BY date ASC")->fetchAll(PDO::FETCH_ASSOC);
// Chart Data: Bookings status distribution
$bookings_status_data = $pdo->query("SELECT status, COUNT(*) as count FROM bookings GROUP BY status")->fetchAll(PDO::FETCH_ASSOC);
// Top Selling Cars (based on approved bookings)
$top_selling_cars = $pdo->query("
SELECT c.make, c.model, COUNT(b.id) as sales
FROM cars c
JOIN bookings b ON c.id = b.car_id
WHERE b.status = 'approved'
GROUP BY c.id, c.make, c.model
ORDER BY sales DESC
LIMIT 5
")->fetchAll(PDO::FETCH_ASSOC);
// Chart Data: Car Status Distribution
$car_status_data = $pdo->query("SELECT status, COUNT(*) as count FROM cars GROUP BY status")->fetchAll(PDO::FETCH_ASSOC);
$projectName = 'Admin Dashboard';
?>
@ -60,37 +50,50 @@ $projectName = 'Admin Dashboard';
<!-- Stat Cards -->
<div class="row g-4 mb-4">
<div class="col-md-6 col-lg-3"><div class="stat-card"><h4>Total Users</h4><p class="fs-2 fw-bold"><?= $stats['users'] ?></p></div></div>
<div class="col-md-6 col-lg-3"><div class="stat-card"><h4>Listed Cars</h4><p class="fs-2 fw-bold"><?= $stats['cars'] ?></p></div></div>
<div class="col-md-6 col-lg-3"><div class="stat-card"><h4>Completed Sales</h4><p class="fs-2 fw-bold"><?= $stats['bookings'] ?></p></div></div>
<div class="col-md-6 col-lg-3"><div class="stat-card"><h4>Pending Bookings</h4><p class="fs-2 fw-bold"><?= $stats['pending_bookings'] ?></p></div></div>
<div class="col-md-6 col-lg-3">
<div class="stat-card border-start border-4 border-primary">
<h6 class="text-muted text-uppercase mb-2">Total Users</h6>
<h2 class="mb-0 fw-bold"><?= number_format($stats['users']) ?></h2>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="stat-card border-start border-4 border-success">
<h6 class="text-muted text-uppercase mb-2">Total Revenue</h6>
<h2 class="mb-0 fw-bold">$<?= number_format($stats['revenue'] ?? 0) ?></h2>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="stat-card border-start border-4 border-info">
<h6 class="text-muted text-uppercase mb-2">Cars Sold</h6>
<h2 class="mb-0 fw-bold"><?= number_format($stats['sales_count']) ?></h2>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="stat-card border-start border-4 border-warning">
<h6 class="text-muted text-uppercase mb-2">Pending Requests</h6>
<h2 class="mb-0 fw-bold"><?= number_format($stats['pending_requests']) ?></h2>
</div>
</div>
</div>
<!-- Charts -->
<div class="row g-4 mb-4">
<div class="col-lg-8">
<div class="card h-100"><div class="card-body"><h5 class="card-title">Sales Over Time (Last 30 Days)</h5><canvas id="salesChart"></canvas></div></div>
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<h5 class="card-title">Sales Trend (Last 30 Days)</h5>
<canvas id="salesChart"></canvas>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card h-100"><div class="card-body"><h5 class="card-title">Bookings Distribution</h5><canvas id="bookingsChart"></canvas></div></div>
</div>
</div>
<!-- Top Lists -->
<div class="card">
<div class="card-header"><h5 class="card-title mb-0">Top 5 Selling Cars</h5></div>
<div class="card-body">
<div class="list-group list-group-flush">
<?php if (empty($top_selling_cars)): ?>
<div class="list-group-item">No sales data available yet.</div>
<?php else: ?>
<?php foreach($top_selling_cars as $c): ?>
<div class="list-group-item d-flex justify-content-between align-items-center">
<?= htmlspecialchars($c['make'] . ' ' . $c['model']) ?>
<span class="badge bg-success rounded-pill"><?= $c['sales'] ?> sales</span>
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<h5 class="card-title">Inventory Status</h5>
<div style="height: 250px;">
<canvas id="statusChart"></canvas>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
@ -99,37 +102,36 @@ $projectName = 'Admin Dashboard';
</div>
<script>
// Chart.js configurations
// Sales Chart
const salesChartCtx = document.getElementById('salesChart').getContext('2d');
new Chart(salesChartCtx, {
type: 'line',
data: {
labels: <?= json_encode(array_column($sales_data, 'date')) ?>,
datasets: [{
label: 'Daily Sales',
data: <?= json_encode(array_column($sales_data, 'count')) ?>,
borderColor: 'var(--primary-color)',
backgroundColor: 'rgba(79, 70, 229, 0.1)',
label: 'Revenue ($)',
data: <?= json_encode(array_column($sales_data, 'total')) ?>,
borderColor: '#198754',
backgroundColor: 'rgba(25, 135, 84, 0.1)',
fill: true,
tension: 0.4
}]
}
});
const bookingsChartCtx = document.getElementById('bookingsChart').getContext('2d');
new Chart(bookingsChartCtx, {
type: 'doughnut',
data: {
labels: <?= json_encode(array_column($bookings_status_data, 'status')) ?>,
datasets: [{
data: <?= json_encode(array_column($bookings_status_data, 'count')) ?>,
backgroundColor: ['#ffc107', '#198754', '#dc3545'] // Pending, Approved, Cancelled
tension: 0.3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
}
options: { responsive: true }
});
// Status Chart
const statusChartCtx = document.getElementById('statusChart').getContext('2d');
new Chart(statusChartCtx, {
type: 'doughnut',
data: {
labels: <?= json_encode(array_column($car_status_data, 'status')) ?>,
datasets: [{
data: <?= json_encode(array_column($car_status_data, 'count')) ?>,
backgroundColor: ['#ffc107', '#0d6efd', '#198754', '#dc3545', '#6c757d']
}]
},
options: { responsive: true, maintainAspectRatio: false }
});
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>

View File

@ -2,36 +2,79 @@
// This is a new file: admin/partials/sidebar.php
$current_page = basename($_SERVER['PHP_SELF']);
?>
<nav class="admin-sidebar">
<a class="navbar-brand mb-4" href="index.php">CarAdmin</a>
<ul class="nav flex-column">
<nav class="admin-sidebar d-flex flex-column p-3 bg-white shadow vh-100" style="width: 280px; position: fixed;">
<a class="navbar-brand mb-4 fs-4 fw-bold text-primary" href="index.php">
<i class="bi bi-speedometer2 me-2"></i> CarAdmin
</a>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a class="nav-link <?php echo ($current_page == 'index.php') ? 'active' : ''; ?>" href="index.php">
<a class="nav-link <?php echo ($current_page == 'index.php') ? 'active' : 'link-dark'; ?>" href="index.php">
<i class="bi bi-grid-1x2-fill me-2"></i> Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php echo ($current_page == 'cars.php') ? 'active' : ''; ?>" href="cars.php">
<i class="bi bi-car-front-fill me-2"></i> Manage Cars
<a class="nav-link <?php echo ($current_page == 'requests.php') ? 'active' : 'link-dark'; ?>" href="requests.php">
<i class="bi bi-inbox-fill me-2"></i> Sales Requests
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php echo ($current_page == 'bookings.php') ? 'active' : ''; ?>" href="bookings.php">
<i class="bi bi-calendar-check-fill me-2"></i> Manage Bookings
<a class="nav-link <?php echo ($current_page == 'cars.php') ? 'active' : 'link-dark'; ?>" href="cars.php">
<i class="bi bi-car-front-fill me-2"></i> Inventory
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php echo ($current_page == 'users.php') ? 'active' : ''; ?>" href="users.php">
<i class="bi bi-people-fill me-2"></i> Manage Users
<a class="nav-link <?php echo ($current_page == 'bookings.php') ? 'active' : 'link-dark'; ?>" href="bookings.php">
<i class="bi bi-cart-check-fill me-2"></i> Sales/Bookings
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php echo ($current_page == 'reviews.php') ? 'active' : ''; ?>" href="reviews.php">
<i class="bi bi-star-half me-2"></i> Manage Reviews
<a class="nav-link <?php echo ($current_page == 'users.php') ? 'active' : 'link-dark'; ?>" href="users.php">
<i class="bi bi-people-fill me-2"></i> Users
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php echo ($current_page == 'reviews.php') ? 'active' : 'link-dark'; ?>" href="reviews.php">
<i class="bi bi-star-half me-2"></i> Reviews
</a>
</li>
</ul>
<div class="mt-auto">
<a class="nav-link" href="../logout.php"><i class="bi bi-box-arrow-left me-2"></i> Logout</a>
<hr>
<div class="dropdown">
<a href="../logout.php" class="d-flex align-items-center link-dark text-decoration-none">
<i class="bi bi-box-arrow-left me-2"></i> <strong>Logout</strong>
</a>
</div>
</nav>
<style>
/* Admin Sidebar Layout Fix */
.admin-wrapper {
display: flex;
}
.admin-sidebar {
z-index: 1000;
}
.admin-main-content {
flex: 1;
margin-left: 280px; /* Width of sidebar */
padding: 2rem;
background-color: #f8f9fa;
min-height: 100vh;
}
@media (max-width: 768px) {
.admin-sidebar {
width: 100%;
height: auto;
position: relative;
}
.admin-main-content {
margin-left: 0;
}
}
.stat-card {
background: white;
padding: 1.5rem;
border-radius: 10px;
box-shadow: 0 0.125rem 0.25rem rgba(0,0,0,0.075);
}
</style>

91
admin/requests.php Normal file
View File

@ -0,0 +1,91 @@
<?php
session_start();
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: ../login.php");
exit();
}
require_once '../db/config.php';
$pdo = db();
// Handle Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['action']) && isset($_POST['car_id'])) {
$status = ($_POST['action'] === 'approve') ? 'approved' : 'rejected';
$stmt = $pdo->prepare("UPDATE cars SET status = ? WHERE id = ?");
$stmt->execute([$status, $_POST['car_id']]);
}
}
// Fetch Pending Cars
$stmt = $pdo->query("
SELECT c.*, u.username as owner
FROM cars c
LEFT JOIN users u ON c.user_id = u.id
WHERE c.status = 'pending'
ORDER BY c.created_at ASC
");
$pending_cars = $stmt->fetchAll(PDO::FETCH_ASSOC);
$projectName = 'Sales Requests';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><?= htmlspecialchars($projectName) ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?= time() ?>">
</head>
<body>
<div class="admin-wrapper">
<?php include 'partials/sidebar.php'; ?>
<main class="admin-main-content">
<h1 class="h2 mb-4">Pending Sale Requests</h1>
<?php if (empty($pending_cars)): ?>
<div class="alert alert-info">No pending requests found.</div>
<?php else: ?>
<div class="table-responsive bg-white rounded shadow-sm">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Image</th>
<th>Car Details</th>
<th>Price</th>
<th>Owner</th>
<th>Submitted</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($pending_cars as $car): ?>
<tr>
<td style="width: 100px;">
<img src="../<?= htmlspecialchars($car['image_url'] ?: 'https://via.placeholder.com/100') ?>" alt="Car" class="img-thumbnail" style="width: 80px; height: 60px; object-fit: cover;">
</td>
<td>
<strong><?= htmlspecialchars($car['make'] . ' ' . $car['model']) ?></strong><br>
<small class="text-muted"><?= htmlspecialchars($car['year']) ?> &bull; <?= number_format($car['mileage']) ?> km</small>
</td>
<td>$<?= number_format($car['price']) ?></td>
<td><?= htmlspecialchars($car['owner'] ?? 'Unknown') ?></td>
<td><?= date('M d, Y', strtotime($car['created_at'])) ?></td>
<td>
<form method="POST" class="d-flex gap-2">
<input type="hidden" name="car_id" value="<?= $car['id'] ?>">
<button type="submit" name="action" value="approve" class="btn btn-success btn-sm"><i class="bi bi-check-lg"></i> Approve</button>
<button type="submit" name="action" value="reject" class="btn btn-danger btn-sm" onclick="return confirm('Reject this listing?')"><i class="bi bi-x-lg"></i> Reject</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -12,50 +12,6 @@ $pdo = db();
$message = '';
$message_type = '';
// Handle POST requests (Booking or Review)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
$userId = $_SESSION['user_id'];
// Booking Logic
if (isset($_POST['book_now'])) {
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare("INSERT INTO bookings (user_id, car_id, status) VALUES (?, ?, 'pending')");
$stmt->execute([$userId, $carId]);
$stmt = $pdo->prepare("UPDATE cars SET status = 'reserved' WHERE id = ? AND status = 'approved'");
$stmt->execute([$carId]);
$pdo->commit();
$message = "Your booking request has been sent! The car is reserved for you pending admin approval.";
$message_type = 'success';
} catch (Exception $e) {
$pdo->rollBack();
error_log("Booking failed: " . $e->getMessage());
$message = "There was an error processing your booking. Please try again.";
$message_type = 'danger';
}
}
// Review Logic
if (isset($_POST['submit_review'])) {
$rating = filter_input(INPUT_POST, 'rating', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 5]]);
$review_text = trim(filter_input(INPUT_POST, 'review', FILTER_SANITIZE_SPECIAL_CHARS));
if ($rating && !empty($review_text)) {
$stmt = $pdo->prepare("INSERT INTO reviews (car_id, user_id, rating, review) VALUES (?, ?, ?, ?)");
$stmt->execute([$carId, $userId, $rating, $review_text]);
$message = "Your review has been submitted and is pending approval.";
$message_type = 'success';
} else {
$message = "Invalid rating or review text.";
$message_type = 'danger';
}
}
}
// Fetch car details
$stmt = $pdo->prepare("SELECT * FROM cars WHERE id = ?");
$stmt->execute([$carId]);
@ -71,125 +27,262 @@ $stmt = $pdo->prepare("SELECT r.*, u.username FROM reviews r JOIN users u ON r.u
$stmt->execute([$carId]);
$reviews = $stmt->fetchAll(PDO::FETCH_ASSOC);
$projectName = htmlspecialchars($car['make'] . ' ' . $car['model']);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $projectName ?> - Car Sells Afghanistan</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>">
</head>
<body>
<?php include 'partials/navbar.php'; ?>
// Handle Review POST
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_review'])) {
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
$rating = filter_input(INPUT_POST, 'rating', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 5]]);
$review_text = trim(filter_input(INPUT_POST, 'review', FILTER_SANITIZE_SPECIAL_CHARS));
if ($rating && !empty($review_text)) {
$stmt = $pdo->prepare("INSERT INTO reviews (car_id, user_id, rating, review) VALUES (?, ?, ?, ?)");
$stmt->execute([$carId, $_SESSION['user_id'], $rating, $review_text]);
$message = "Your review has been submitted and is pending approval.";
$message_type = 'success';
} else {
$message = "Invalid rating or review text.";
$message_type = 'danger';
}
}
<main class="section-padding">
$pageTitle = htmlspecialchars($car['make'] . ' ' . $car['model']);
include 'partials/header.php';
?>
<main class="py-5 bg-light">
<div class="container">
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item"><a href="car_list.php">Cars</a></li>
<li class="breadcrumb-item active" aria-current="page"><?= $pageTitle ?></li>
</ol>
</nav>
<div class="row g-5">
<!-- Car Image Gallery -->
<div class="col-lg-7">
<img src="<?= htmlspecialchars($car['image_url'] ?: 'https://via.placeholder.com/800x600?text=Car+Image') ?>" class="img-fluid rounded shadow-lg w-100" alt="<?= $projectName ?>">
<div class="col-lg-8">
<div class="card border-0 shadow-lg overflow-hidden rounded-4">
<div class="position-relative">
<img src="<?= htmlspecialchars($car['image_url'] ?: 'https://via.placeholder.com/800x600?text=Car+Image') ?>" class="img-fluid w-100 object-fit-cover" style="max-height: 500px;" alt="<?= $pageTitle ?>">
<?php if ($car['status'] === 'sold'): ?>
<div class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center bg-dark bg-opacity-75 text-white">
<h1 class="display-1 fw-bold text-uppercase border-4 border border-white p-4">SOLD</h1>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Car Details & Booking -->
<div class="col-lg-5">
<h1 class="h2 mb-2"><?= $projectName ?></h1>
<p class="text-muted fs-5 mb-4"><i class="bi bi-geo-alt-fill me-2"></i><?= htmlspecialchars($car['city'] . ', ' . $car['province']) ?></p>
<p class="lead mb-4"><?= htmlspecialchars($car['description']) ?></p>
<div class="col-lg-4">
<div class="card border-0 shadow-sm rounded-4 h-100">
<div class="card-body p-4 d-flex flex-column">
<h1 class="h2 fw-bold mb-1"><?= $pageTitle ?></h1>
<p class="text-muted fs-6 mb-3"><i class="bi bi-geo-alt-fill me-1 text-danger"></i><?= htmlspecialchars($car['city'] . ', ' . $car['province']) ?></p>
<div class="d-flex align-items-center justify-content-between mb-4 p-3 bg-light rounded-3">
<span class="text-muted fw-semibold">Price</span>
<span class="h3 fw-bold text-primary mb-0">$<?= number_format($car['price']) ?></span>
</div>
<div class="card p-4 mb-4">
<h3 class="h5 mb-3">Key Specifications</h3>
<div class="row g-3 fs-6">
<div class="col-6"><i class="bi bi-calendar-event me-2 text-primary"></i> <strong>Year:</strong> <?= htmlspecialchars($car['year']) ?></div>
<div class="col-6"><i class="bi bi-palette me-2 text-primary"></i> <strong>Color:</strong> <?= htmlspecialchars($car['color'] ?? 'N/A') ?></div>
<div class="col-6"><i class="bi bi-speedometer2 me-2 text-primary"></i> <strong>Mileage:</strong> <?= number_format($car['mileage']) ?> km</div>
<div class="col-6"><i class="bi bi-fuel-pump me-2 text-primary"></i> <strong>Fuel:</strong> Petrol</div>
<div class="row g-3 fs-6 mb-4">
<div class="col-6">
<div class="d-flex align-items-center">
<div class="icon-square bg-primary bg-opacity-10 text-primary rounded-3 me-2 p-2">
<i class="bi bi-calendar-event"></i>
</div>
<div>
<small class="text-muted d-block">Year</small>
<strong><?= htmlspecialchars($car['year']) ?></strong>
</div>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center">
<div class="icon-square bg-success bg-opacity-10 text-success rounded-3 me-2 p-2">
<i class="bi bi-speedometer2"></i>
</div>
<div>
<small class="text-muted d-block">Mileage</small>
<strong><?= number_format($car['mileage']) ?> km</strong>
</div>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center">
<div class="icon-square bg-info bg-opacity-10 text-info rounded-3 me-2 p-2">
<i class="bi bi-palette"></i>
</div>
<div>
<small class="text-muted d-block">Color</small>
<strong><?= htmlspecialchars($car['color'] ?? 'N/A') ?></strong>
</div>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center">
<div class="icon-square bg-warning bg-opacity-10 text-warning rounded-3 me-2 p-2">
<i class="bi bi-fuel-pump"></i>
</div>
<div>
<small class="text-muted d-block">Fuel</small>
<strong>Petrol</strong>
</div>
</div>
</div>
</div>
<div class="mb-4">
<h6 class="fw-bold">Description</h6>
<p class="text-muted small mb-0"><?= nl2br(htmlspecialchars($car['description'])) ?></p>
</div>
<div class="mt-auto">
<?php if (!empty($message)): ?>
<div class="alert alert-<?= $message_type ?> mb-3"><?= $message ?></div>
<?php endif; ?>
<?php if ($car['status'] === 'approved'): ?>
<?php if (isset($_SESSION['user_id'])): ?>
<button type="button" class="btn btn-primary btn-lg w-100 py-3 fw-bold rounded-pill shadow-sm" data-bs-toggle="modal" data-bs-target="#buyModal">
<i class="bi bi-cart-check me-2"></i> Buy Now
</button>
<?php else: ?>
<a href="login.php" class="btn btn-primary btn-lg w-100 py-3 fw-bold rounded-pill shadow-sm">Login to Buy</a>
<?php endif; ?>
<?php elseif ($car['status'] === 'sold'): ?>
<button class="btn btn-secondary btn-lg w-100 py-3 fw-bold rounded-pill disabled" disabled>SOLD OUT</button>
<?php else: ?>
<div class="alert alert-info text-center">Status: <strong class="text-capitalize"><?= htmlspecialchars($car['status']) ?></strong></div>
<?php endif; ?>
</div>
</div>
</div>
<div class="text-end mb-4">
<span class="display-5 fw-bold" style="color: var(--secondary-color);">$<?= number_format($car['price']) ?></span>
</div>
<?php if (!empty($message)): ?>
<div class="alert alert-<?= $message_type ?>"><?= $message ?></div>
<?php endif; ?>
<?php if ($car['status'] === 'approved'): ?>
<form method="POST" class="d-grid">
<button type="submit" name="book_now" class="btn btn-primary btn-lg">Book This Car Now</button>
</form>
<?php else: ?>
<div class="alert alert-info text-center">This car is currently <strong class="text-capitalize"><?= htmlspecialchars($car['status']) ?></strong> and cannot be booked.</div>
<?php endif; ?>
</div>
</div>
<hr class="my-5">
<div class="row mt-5">
<div class="col-12">
<div class="card border-0 shadow-sm rounded-4">
<div class="card-body p-4 p-lg-5">
<h2 class="h4 fw-bold mb-4">Customer Reviews</h2>
<!-- Reviews Section from previous steps remains unchanged -->
<div class="row justify-content-center">
<div class="col-lg-8">
<h2 class="text-center mb-5">Customer Reviews</h2>
<?php if (isset($_SESSION['user_id'])): ?>
<div class="bg-light p-4 rounded-3 mb-5">
<h5 class="fw-bold mb-3">Leave a Review</h5>
<form method="POST">
<div class="mb-3">
<label for="rating" class="form-label text-muted small text-uppercase fw-bold">Rating</label>
<div class="input-group">
<select name="rating" id="rating" class="form-select" required>
<option value="5">★★★★★ (Excellent)</option>
<option value="4">★★★★☆ (Great)</option>
<option value="3">★★★☆☆ (Good)</option>
<option value="2">★★☆☆☆ (Fair)</option>
<option value="1">★☆☆☆☆ (Poor)</option>
</select>
</div>
</div>
<div class="mb-3">
<label for="review" class="form-label text-muted small text-uppercase fw-bold">Review</label>
<textarea name="review" id="review" class="form-control" rows="3" placeholder="Share your experience..." required></textarea>
</div>
<button type="submit" name="submit_review" class="btn btn-dark px-4 rounded-pill">Submit Review</button>
</form>
</div>
<?php else: ?>
<div class="alert alert-light border text-center mb-5">
<a href="login.php" class="fw-bold text-decoration-none">Log in</a> to write a review.
</div>
<?php endif; ?>
<?php if (isset($_SESSION['user_id'])): ?>
<div class="card mb-5">
<div class="card-body p-4">
<h4 class="card-title mb-3">Leave Your Feedback</h4>
<form method="POST">
<div class="mb-3">
<label for="rating" class="form-label">Your Rating (1-5)</label>
<select name="rating" id="rating" class="form-select" required>
<option value="">Select a rating</option>
<option value="5">★★★★★ (Excellent)</option>
<option value="4">★★★★☆ (Great)</option>
<option value="3">★★★☆☆ (Good)</option>
<option value="2">★★☆☆☆ (Fair)</option>
<option value="1">★☆☆☆☆ (Poor)</option>
</select>
<?php if (empty($reviews)): ?>
<div class="text-center text-muted py-5">
<i class="bi bi-chat-dots fs-1 mb-3 d-block text-secondary"></i>
<p>No reviews yet. Be the first to share your thoughts!</p>
</div>
<div class="mb-3">
<label for="review" class="form-label">Your Review</label>
<textarea name="review" id="review" class="form-control" rows="4" placeholder="Share your experience with this car..." required></textarea>
<?php else: ?>
<div class="row g-4">
<?php foreach ($reviews as $review): ?>
<div class="col-md-6">
<div class="card h-100 border bg-light">
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center me-3" style="width: 40px; height: 40px; font-weight: bold;">
<?= strtoupper(substr($review['username'], 0, 1)) ?>
</div>
<div>
<h6 class="mb-0 fw-bold"><?= htmlspecialchars($review['username']) ?></h6>
<small class="text-muted"><?= date("M d, Y", strtotime($review['created_at'])) ?></small>
</div>
<div class="ms-auto text-warning">
<?= str_repeat('★', $review['rating']) . str_repeat('☆', 5 - $review['rating']) ?>
</div>
</div>
<p class="card-text text-muted"><?= nl2br(htmlspecialchars($review['review'])) ?></p>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<button type="submit" name="submit_review" class="btn btn-primary">Submit Review</button>
</form>
<?php endif; ?>
</div>
</div>
<?php else: ?>
<p class="text-center"><a href="login.php">Log in</a> to share your experience.</p>
<?php endif; ?>
<?php if (empty($reviews)): ?>
<p class="text-center text-muted">Be the first to write a review for this car!</p>
<?php else: ?>
<?php foreach ($reviews as $review): ?>
<div class="card mb-3">
<div class="card-body d-flex">
<div class="flex-shrink-0 me-3">
<img src="https://i.pravatar.cc/60?u=<?= htmlspecialchars($review['username']) ?>" alt="" class="rounded-circle">
</div>
<div>
<h5 class="mt-0 mb-1"><?= htmlspecialchars($review['username']) ?></h5>
<div class="text-warning mb-2">
<?= str_repeat('★', $review['rating']) . str_repeat('☆', 5 - $review['rating']) ?>
</div>
<p class="mb-1"><?= nl2br(htmlspecialchars($review['review'])) ?></p>
<small class="text-muted"><?= date("F j, Y", strtotime($review['created_at'])) ?></small>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</main>
<!-- Buy Modal -->
<div class="modal fade" id="buyModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow-lg">
<form action="purchase_process.php" method="POST">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title fw-bold">Complete Purchase</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<input type="hidden" name="car_id" value="<?= $car['id'] ?>">
<div class="text-center mb-4">
<h4 class="text-primary fw-bold">$<?= number_format($car['price']) ?></h4>
<p class="text-muted"><?= htmlspecialchars($car['title']) ?></p>
</div>
<div class="form-floating mb-3">
<select name="province" class="form-select" id="provinceSelect" required>
<option value="">Select Province</option>
<option value="Kabul">Kabul</option>
<option value="Herat">Herat</option>
<option value="Kandahar">Kandahar</option>
<option value="Mazar-i-Sharif">Mazar-i-Sharif</option>
<option value="Jalalabad">Jalalabad</option>
<option value="Other">Other</option>
</select>
<label for="provinceSelect">Bank Province</label>
</div>
<div class="form-floating mb-3">
<input type="text" name="account_number" class="form-control" id="accNum" required placeholder="Account Number">
<label for="accNum">Bank Account Number</label>
</div>
<div class="alert alert-warning d-flex align-items-center small" role="alert">
<i class="bi bi-info-circle-fill me-2 fs-5"></i>
<div>By clicking "Confirm", you agree to the terms of sale. This action cannot be undone.</div>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary fw-bold px-4">Confirm Purchase</button>
</div>
</form>
</div>
</div>
</div>
<?php include 'partials/footer.php'; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>

View File

@ -1,11 +1,10 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
$pdo = db();
// Filtering Logic
$whereClauses = ["status = 'approved'"];
$whereClauses = ["status IN ('approved', 'sold')"];
$params = [];
if (!empty($_GET['make'])) {
@ -35,30 +34,19 @@ $sql = "SELECT * FROM cars";
if (!empty($whereClauses)) {
$sql .= " WHERE " . implode(' AND ', $whereClauses);
}
$sql .= " ORDER BY created_at DESC";
$sql .= " ORDER BY CASE WHEN status = 'approved' THEN 1 ELSE 2 END, created_at DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$cars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch distinct makes and provinces for filter dropdowns
$makes = $pdo->query("SELECT DISTINCT make FROM cars WHERE status = 'approved' ORDER BY make ASC")->fetchAll(PDO::FETCH_COLUMN);
$provinces = $pdo->query("SELECT DISTINCT province FROM cars WHERE status = 'approved' AND province IS NOT NULL ORDER BY province ASC")->fetchAll(PDO::FETCH_COLUMN);
$makes = $pdo->query("SELECT DISTINCT make FROM cars WHERE status IN ('approved', 'sold') ORDER BY make ASC")->fetchAll(PDO::FETCH_COLUMN);
$provinces = $pdo->query("SELECT DISTINCT province FROM cars WHERE status IN ('approved', 'sold') AND province IS NOT NULL ORDER BY province ASC")->fetchAll(PDO::FETCH_COLUMN);
$projectName = 'All Car Listings';
$pageTitle = 'All Car Listings';
include 'partials/header.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($projectName) ?> - Car Sells Afghanistan</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>">
</head>
<body>
<?php include 'partials/navbar.php'; ?>
<header class="bg-light py-5">
<div class="container text-center">
@ -67,10 +55,10 @@ $projectName = 'All Car Listings';
</div>
</header>
<main class="container section-padding">
<main class="container section-padding py-5">
<div class="row g-5">
<aside class="col-lg-3">
<div class="card p-4 sticky-top" style="top: 2rem;">
<div class="card p-4 sticky-top border-0 shadow-sm" style="top: 2rem;">
<h4 class="mb-4">Filter Results</h4>
<form method="GET">
<div class="mb-3">
@ -115,8 +103,15 @@ $projectName = 'All Car Listings';
<?php if (!empty($cars)): ?>
<?php foreach ($cars as $car): ?>
<div class="col-md-6 col-xl-4 d-flex align-items-stretch">
<div class="card w-100">
<img src="<?= htmlspecialchars($car['image_url'] ?: 'https://via.placeholder.com/400x300?text=No+Image') ?>" class="card-img-top" alt="<?= htmlspecialchars($car['make'] . ' ' . $car['model']) ?>">
<div class="card w-100 border-0 shadow-sm">
<div class="position-relative">
<img src="<?= htmlspecialchars($car['image_url'] ?: 'https://via.placeholder.com/400x300?text=No+Image') ?>" class="card-img-top" alt="<?= htmlspecialchars($car['make'] . ' ' . $car['model']) ?>" style="height: 200px; object-fit: cover;">
<?php if ($car['status'] === 'sold'): ?>
<div class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center bg-dark bg-opacity-75 text-white">
<h2 class="fw-bold text-uppercase">SOLD</h2>
</div>
<?php endif; ?>
</div>
<div class="card-body d-flex flex-column">
<h5 class="card-title"><?= htmlspecialchars($car['make'] . ' ' . $car['model']) ?></h5>
<p class="card-text text-muted"><i class="bi bi-geo-alt-fill me-1"></i> <?= htmlspecialchars($car['city'] . ', ' . $car['province']) ?></p>
@ -124,8 +119,8 @@ $projectName = 'All Car Listings';
<span><i class="bi bi-calendar me-1"></i> <?= htmlspecialchars($car['year']) ?></span>
<span><i class="bi bi-speedometer2 me-1"></i> <?= number_format($car['mileage']) ?> km</span>
</div>
<h4 class="mt-auto mb-3 text-end fw-bold" style="color: var(--secondary-color);">$<?= number_format($car['price']) ?></h4>
<a href="car_detail.php?id=<?= $car['id'] ?>" class="btn btn-primary">View Details</a>
<h4 class="mt-auto mb-3 text-end fw-bold" style="color: var(--bs-primary);">$<?= number_format($car['price']) ?></h4>
<a href="car_detail.php?id=<?= $car['id'] ?>" class="btn btn-primary <?= ($car['status'] === 'sold') ? 'disabled' : '' ?>">View Details</a>
</div>
</div>
</div>
@ -145,7 +140,7 @@ $projectName = 'All Car Listings';
</div>
</main>
<?php include 'partials/footer.php'; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<?php include 'partials/footer.php'; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,7 @@
ALTER TABLE cars ADD COLUMN user_id INT NULL;
-- We use SET NULL so if a user is deleted, their car listings remain (maybe owned by admin)
ALTER TABLE cars ADD CONSTRAINT fk_cars_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE bookings ADD COLUMN bank_province VARCHAR(100) NULL;
ALTER TABLE bookings ADD COLUMN bank_account_number VARCHAR(100) NULL;
ALTER TABLE bookings ADD COLUMN sale_price DECIMAL(10, 2) NULL;

View File

@ -1,5 +1,4 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
// Fetch Featured Cars
@ -11,37 +10,11 @@ try {
$cars = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
error_log("DB Error: " . $e->getMessage());
// You can set a user-friendly error message here if you want
}
// Meta variables from environment - important for platform integration
$projectName = getenv('PROJECT_NAME') ?: 'Car Sells in Afghanistan';
$projectDesc = getenv('PROJECT_DESCRIPTION') ?: 'The best marketplace for buying and selling cars in Afghanistan.';
$projectImage = getenv('PROJECT_IMAGE_URL') ?: 'https://images.pexels.com/photos/120049/pexels-photo-120049.jpeg?auto=compress&cs=tinysrgb&w=1200';
$pageTitle = "Home";
include 'partials/header.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($projectName); ?> - Modern Car Marketplace</title>
<meta name="description" content="<?php echo htmlspecialchars($projectDesc); ?>">
<!-- Open Graph / Social Media Meta -->
<meta property="og:title" content="<?php echo htmlspecialchars($projectName); ?>">
<meta property="og:description" content="<?php echo htmlspecialchars($projectDesc); ?>">
<meta property="og:image" content="<?php echo htmlspecialchars($projectImage); ?>">
<!-- Libs -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<?php include 'partials/navbar.php'; ?>
<!-- Hero Section -->
<header class="container-fluid vh-100 d-flex align-items-center justify-content-center text-white text-center" style="background: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url('https://images.pexels.com/photos/3764984/pexels-photo-3764984.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') no-repeat center center/cover;">
@ -54,7 +27,7 @@ $projectImage = getenv('PROJECT_IMAGE_URL') ?: 'https://images.pexels.com/photos
</header>
<!-- Featured Cars Section -->
<section id="featured-cars" class="section-padding">
<section id="featured-cars" class="section-padding py-5">
<div class="container">
<div class="text-center mb-5">
<h2 class="h1">Featured Vehicles</h2>
@ -64,13 +37,20 @@ $projectImage = getenv('PROJECT_IMAGE_URL') ?: 'https://images.pexels.com/photos
<?php if (!empty($cars)): ?>
<?php foreach (array_slice($cars, 0, 4) as $car): // Show only 4 featured ?>
<div class="col-md-6 col-lg-3 d-flex align-items-stretch">
<div class="card w-100">
<img src="<?php echo htmlspecialchars($car['image_url'] ?: 'https://via.placeholder.com/400x300?text=No+Image'); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($car['make'].[ 'model']); ?>">
<div class="card w-100 shadow-sm border-0">
<div class="position-relative">
<img src="<?php echo htmlspecialchars($car['image_url'] ?: 'https://via.placeholder.com/400x300?text=No+Image'); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($car['make'] . ' ' . $car['model']); ?>" style="height: 200px; object-fit: cover;">
<?php if ($car['status'] == 'sold'): ?>
<div class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center bg-dark bg-opacity-75 text-white">
<h2 class="fw-bold text-uppercase">SOLD</h2>
</div>
<?php endif; ?>
</div>
<div class="card-body d-flex flex-column">
<h5 class="card-title"><?php echo htmlspecialchars($car['make'] . ' ' . $car['model']); ?></h5>
<p class="card-text text-muted"><?php echo htmlspecialchars($car['year']); ?> &bull; <?php echo number_format($car['mileage']); ?> km</p>
<h4 class="mt-auto mb-3 text-end">$<?php echo number_format($car['price']); ?></h4>
<a href="car_detail.php?id=<?php echo $car['id']; ?>" class="btn btn-primary">View Details</a>
<p class="card-text text-muted small"><?php echo htmlspecialchars($car['year']); ?> &bull; <?php echo number_format($car['mileage']); ?> km</p>
<h4 class="mt-auto mb-3 text-primary">$<?php echo number_format($car['price']); ?></h4>
<a href="car_detail.php?id=<?php echo $car['id']; ?>" class="btn btn-outline-primary w-100">View Details</a>
</div>
</div>
</div>
@ -85,7 +65,7 @@ $projectImage = getenv('PROJECT_IMAGE_URL') ?: 'https://images.pexels.com/photos
</section>
<!-- How It Works Section -->
<section class="section-padding bg-light">
<section class="section-padding py-5 bg-light">
<div class="container">
<div class="text-center mb-5">
<h2 class="h1">How It Works</h2>
@ -93,30 +73,32 @@ $projectImage = getenv('PROJECT_IMAGE_URL') ?: 'https://images.pexels.com/photos
</div>
<div class="row g-4 text-center">
<div class="col-md-4">
<div class="card p-4 h-100">
<div class="card p-4 h-100 border-0 shadow-sm">
<i class="bi bi-search fs-1 text-primary mb-3"></i>
<h3>1. Find Your Car</h3>
<p>Browse our curated selection of high-quality vehicles. Use filters to narrow down your perfect match.</p>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 h-100">
<div class="card p-4 h-100 border-0 shadow-sm">
<i class="bi bi-journal-check fs-1 text-primary mb-3"></i>
<h3>2. Book a Test Drive</h3>
<p>Schedule a test drive online. We'll confirm your appointment and prepare the vehicle for you.</p>
<h3>2. Request Purchase</h3>
<p>Found a car you like? Request to purchase it directly through our platform safely.</p>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 h-100">
<div class="card p-4 h-100 border-0 shadow-sm">
<i class="bi bi-patch-check-fill fs-1 text-primary mb-3"></i>
<h3>3. Secure Your Deal</h3>
<p>Finalize your purchase with our secure payment system and drive away in your new car with confidence.</p>
<p>Finalize your purchase with our secure payment system and drive away in your new car.</p>
</div>
</div>
</div>
</div>
</section>
</main> <!-- End Main -->
<?php include 'partials/footer.php'; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>

View File

@ -35,37 +35,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Note: The 'password' column stores the hash
if ($user && password_verify($password, $user['password'])) {
if (isset($user['status']) && $user['status'] !== 'active' && isset($user['status'])) {
// Status column is not in the mandatory schema but might remain if I didn't drop it?
// The prompt asked for specific columns. I will assume only those columns exist.
// So I should probably remove the status check unless I add status to the schema.
// The prompt schema for users: id, username, password, role, created_at. NO STATUS.
// I will remove the status check to be safe.
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
// Redirect to the appropriate dashboard
if ($user['role'] === 'admin') {
header("Location: admin/index.php");
} else {
header("Location: dashboard.php");
}
exit();
} else {
// If status column exists and is checked above...
// Re-implementing logic:
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
if ($user['role'] === 'admin') {
header("Location: admin/index.php");
} else {
header("Location: dashboard.php");
}
exit();
if (isset($user['status']) && $user['status'] !== 'active') {
// Kept specific status check logic if status column existed, but since schema is simple, this block is mostly for safety if schema evolves.
// Current schema doesn't have status, but if it did, we'd check it.
// The setup_project.php removed the status column from users table to fit the simple requirements.
// So we proceed.
}
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
// Redirect to the appropriate dashboard
if ($user['role'] === 'admin') {
header("Location: admin/index.php");
} else {
header("Location: dashboard.php");
}
exit();
} else {
$errors[] = 'Invalid login credentials.';
}
@ -105,8 +93,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<form action="login.php" method="POST">
<div class="mb-4">
<label for="username" class="form-label fw-semibold">Username</label>
<input type="text" id="username" name="username" class="form-control form-control-lg" placeholder="admin" required value="<?php echo isset($username) ? htmlspecialchars($username) : ''; ?>">
<label for="username" class="form-label fw-semibold">Username or Email</label>
<input type="text" id="username" name="username" class="form-control form-control-lg" placeholder="admin@gmail.com" required value="<?php echo isset($username) ? htmlspecialchars($username) : ''; ?>">
</div>
<div class="mb-4">
<label for="password" class="form-label fw-semibold">Password</label>

44
partials/header.php Normal file
View File

@ -0,0 +1,44 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$projectName = getenv('PROJECT_NAME') ?: 'Car Sells in Afghanistan';
$projectDesc = getenv('PROJECT_DESCRIPTION') ?: 'The best marketplace for buying and selling cars in Afghanistan.';
$projectImage = getenv('PROJECT_IMAGE_URL') ?: 'https://images.pexels.com/photos/120049/pexels-photo-120049.jpeg?auto=compress&cs=tinysrgb&w=1200';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo isset($pageTitle) ? htmlspecialchars($pageTitle) . ' - ' . htmlspecialchars($projectName) : htmlspecialchars($projectName); ?></title>
<meta name="description" content="<?php echo htmlspecialchars($projectDesc); ?>">
<!-- Open Graph / Social Media Meta -->
<meta property="og:title" content="<?php echo htmlspecialchars($projectName); ?>">
<meta property="og:description" content="<?php echo htmlspecialchars($projectDesc); ?>">
<meta property="og:image" content="<?php echo htmlspecialchars($projectImage); ?>">
<!-- Libs -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<style>
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1;
}
footer {
margin-top: auto;
}
</style>
</head>
<body>
<?php include __DIR__ . '/navbar.php'; ?>
<main>

57
purchase_process.php Normal file
View File

@ -0,0 +1,57 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_SESSION['user_id'])) {
header("Location: car_list.php");
exit();
}
$pdo = db();
$carId = $_POST['car_id'] ?? 0;
$province = $_POST['province'] ?? '';
$account = $_POST['account_number'] ?? '';
$userId = $_SESSION['user_id'];
if (empty($carId) || empty($province) || empty($account)) {
die("Invalid input.");
}
// Check if car is available
$stmt = $pdo->prepare("SELECT price, status FROM cars WHERE id = ?");
$stmt->execute([$carId]);
$car = $stmt->fetch();
if (!$car || $car['status'] !== 'approved') {
die("Error: This car is no longer available for purchase.");
}
try {
$pdo->beginTransaction();
// 1. Mark car as sold
// We check status again in WHERE clause to prevent race conditions
$stmt = $pdo->prepare("UPDATE cars SET status = 'sold' WHERE id = ? AND status = 'approved'");
$stmt->execute([$carId]);
if ($stmt->rowCount() === 0) {
throw new Exception("Car was just sold to someone else.");
}
// 2. Create Booking/Sale Record
$stmt = $pdo->prepare("INSERT INTO bookings (user_id, car_id, status, booking_date, bank_province, bank_account_number, sale_price) VALUES (?, ?, 'approved', NOW(), ?, ?, ?)");
$stmt->execute([$userId, $carId, $province, $account, $car['price']]);
$bookingId = $pdo->lastInsertId();
$pdo->commit();
header("Location: receipt.php?id=" . $bookingId);
exit();
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
die("Purchase failed: " . $e->getMessage());
}

111
receipt.php Normal file
View File

@ -0,0 +1,111 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
if (!isset($_SESSION['user_id']) || empty($_GET['id'])) {
header("Location: index.php");
exit();
}
$pdo = db();
$bookingId = $_GET['id'];
// Fetch Sale Details
$stmt = $pdo->prepare("
SELECT b.*, c.make, c.model, c.year, c.price, c.mileage, u.username as buyer_name, u.role
FROM bookings b
JOIN cars c ON b.car_id = c.id
JOIN users u ON b.user_id = u.id
WHERE b.id = ?
");
$stmt->execute([$bookingId]);
$sale = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$sale || ($sale['user_id'] != $_SESSION['user_id'] && $_SESSION['role'] !== 'admin')) {
die("Receipt not found or access denied.");
}
$pageTitle = "Sale Receipt";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Receipt #<?= $sale['id'] ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { background-color: #f8f9fa; }
.receipt-container {
max-width: 800px;
margin: 50px auto;
background: white;
padding: 40px;
box-shadow: 0 0 15px rgba(0,0,0,0.1);
}
@media print {
body { background: white; }
.receipt-container { box-shadow: none; margin: 0; padding: 0; }
.no-print { display: none; }
}
</style>
</head>
<body>
<div class="container">
<div class="receipt-container">
<div class="text-center mb-5">
<h1 class="display-6 fw-bold text-primary">Car Sells Afghanistan</h1>
<p class="text-muted">Official Sale Receipt</p>
</div>
<div class="row mb-4">
<div class="col-6">
<h5 class="fw-bold">Buyer Details</h5>
<p class="mb-0">Name: <strong><?= htmlspecialchars($sale['buyer_name']) ?></strong></p>
<p class="mb-0">Bank Province: <?= htmlspecialchars($sale['bank_province']) ?></p>
<p>Account: ****<?= substr($sale['bank_account_number'], -4) ?></p>
</div>
<div class="col-6 text-end">
<h5 class="fw-bold">Receipt Info</h5>
<p class="mb-0">Receipt #: <?= str_pad($sale['id'], 6, '0', STR_PAD_LEFT) ?></p>
<p class="mb-0">Date: <?= date('F j, Y', strtotime($sale['booking_date'])) ?></p>
<p>Status: <span class="badge bg-success text-uppercase">Paid</span></p>
</div>
</div>
<table class="table table-bordered mb-4">
<thead class="table-light">
<tr>
<th>Description</th>
<th class="text-end">Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<strong><?= htmlspecialchars($sale['year'] . ' ' . $sale['make'] . ' ' . $sale['model']) ?></strong><br>
<small class="text-muted">Mileage: <?= number_format($sale['mileage']) ?> km</small>
</td>
<td class="text-end">$<?= number_format($sale['sale_price'] ?? $sale['price'], 2) ?></td>
</tr>
<tr>
<td class="text-end fw-bold">Total</td>
<td class="text-end fw-bold">$<?= number_format($sale['sale_price'] ?? $sale['price'], 2) ?></td>
</tr>
</tbody>
</table>
<div class="text-center mt-5 mb-4">
<p class="lead">Thank you for your business!</p>
<p class="small text-muted">This receipt is electronically generated and valid without signature.</p>
</div>
<div class="text-center no-print mt-4">
<button onclick="window.print()" class="btn btn-primary btn-lg"><i class="bi bi-printer"></i> Print Receipt</button>
<a href="index.php" class="btn btn-link">Back to Home</a>
</div>
</div>
</div>
</body>
</html>

159
sell_car.php Normal file
View File

@ -0,0 +1,159 @@
<?php
require_once __DIR__ . '/db/config.php';
// Ensure session is started (handled by header usually but we need it for check before header)
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit;
}
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = db();
// Validate inputs
$make = $_POST['make'] ?? '';
$model = $_POST['model'] ?? '';
$year = $_POST['year'] ?? '';
$price = $_POST['price'] ?? '';
$mileage = $_POST['mileage'] ?? '';
$color = $_POST['color'] ?? '';
$province = $_POST['province'] ?? '';
$city = $_POST['city'] ?? '';
$description = $_POST['description'] ?? '';
$image_url = '';
if (empty($make) || empty($model) || empty($price)) {
$error = "Make, Model, and Price are required.";
} else {
// Handle Image Upload
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$uploadDir = 'assets/images/uploads/';
$fileName = uniqid() . '_' . basename($_FILES['image']['name']);
$targetPath = $uploadDir . $fileName;
// Check if image file is a actual image or fake image
$check = getimagesize($_FILES['image']['tmp_name']);
if($check !== false) {
if (move_uploaded_file($_FILES['image']['tmp_name'], $targetPath)) {
$image_url = $targetPath;
} else {
$error = "Sorry, there was an error uploading your file.";
}
} else {
$error = "File is not an image.";
}
} elseif (!empty($_POST['image_url_input'])) {
$image_url = $_POST['image_url_input'];
}
if (empty($error)) {
try {
$stmt = $pdo->prepare("INSERT INTO cars (user_id, make, model, year, mileage, price, color, province, city, description, image_url, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending')");
$stmt->execute([
$_SESSION['user_id'], $make, $model, $year, $mileage, $price, $color, $province, $city, $description, $image_url
]);
$success = "Your car has been submitted for approval!";
} catch (PDOException $e) {
$error = "Database Error: " . $e->getMessage();
}
}
}
}
$pageTitle = "Sell Your Car";
include 'partials/header.php';
?>
<div class="container section-padding py-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card shadow border-0">
<div class="card-body p-5">
<h2 class="text-center mb-4">Sell Your Car</h2>
<p class="text-center text-muted mb-5">Fill in the details below to list your car for sale. Our team will review your listing shortly.</p>
<?php if ($error): ?>
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Make *</label>
<input type="text" name="make" class="form-control" required placeholder="e.g. Toyota">
</div>
<div class="col-md-6">
<label class="form-label">Model *</label>
<input type="text" name="model" class="form-control" required placeholder="e.g. Camry">
</div>
<div class="col-md-4">
<label class="form-label">Year</label>
<input type="number" name="year" class="form-control" placeholder="2020">
</div>
<div class="col-md-4">
<label class="form-label">Mileage (km)</label>
<input type="number" name="mileage" class="form-control" placeholder="50000">
</div>
<div class="col-md-4">
<label class="form-label">Price ($) *</label>
<input type="number" name="price" class="form-control" required placeholder="15000">
</div>
<div class="col-md-6">
<label class="form-label">Color</label>
<input type="text" name="color" class="form-control" placeholder="White">
</div>
<div class="col-md-6">
<label class="form-label">Province</label>
<select name="province" class="form-select">
<option value="">Select Province</option>
<option value="Kabul">Kabul</option>
<option value="Herat">Herat</option>
<option value="Kandahar">Kandahar</option>
<option value="Mazar-i-Sharif">Mazar-i-Sharif</option>
<option value="Jalalabad">Jalalabad</option>
<option value="Other">Other</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">City</label>
<input type="text" name="city" class="form-control" placeholder="City Name">
</div>
<div class="col-md-12">
<label class="form-label">Description</label>
<textarea name="description" class="form-control" rows="4" placeholder="Describe the condition, features, etc."></textarea>
</div>
<div class="col-12">
<label class="form-label">Car Image</label>
<div class="input-group mb-2">
<input type="file" name="image" class="form-control" accept="image/*">
</div>
<div class="form-text">Or provide an image URL below if you prefer:</div>
<input type="url" name="image_url_input" class="form-control mt-2" placeholder="https://example.com/car.jpg">
</div>
<div class="col-12 mt-4">
<button type="submit" class="btn btn-primary w-100 py-3 fw-bold">Submit Listing</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<?php include 'partials/footer.php'; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -2,9 +2,8 @@
// setup_project.php
// This script sets up the database tables and data as per the requirements.
require_once 'includes/functions.php'; // Includes db/config.php and helper functions
require_once 'includes/functions.php';
// 1. Connect to Database
try {
$pdo = db();
echo "Database connection established.\n";
@ -12,45 +11,33 @@ try {
die("Database connection failed: " . $e->getMessage() . "\n");
}
// Note: We cannot create a new database 'car_dealership' because of restricted privileges.
// We are using the existing database provided by the environment.
try {
// 2. Drop existing tables to ensure a clean state (Idempotency)
// We drop bookings and reviews first if they exist to avoid foreign key constraints issues (if any)
// although the prompt didn't ask for bookings/reviews, existing app has them.
// I should probably drop them to strictly follow "create necessary tables".
// But if I drop them, the admin dashboard might break if it queries them.
// The prompt says "Create all necessary tables...". It lists cars and users.
// I will drop cars and users. If foreign keys exist on bookings/reviews pointing to cars/users, DROP will fail or cascade depending on setup.
// I'll use SET FOREIGN_KEY_CHECKS=0 to be safe.
// 2. Drop existing tables to ensure a clean state
$pdo->exec("SET FOREIGN_KEY_CHECKS=0");
$pdo->exec("DROP TABLE IF EXISTS reviews");
$pdo->exec("DROP TABLE IF EXISTS bookings");
$pdo->exec("DROP TABLE IF EXISTS cars");
$pdo->exec("DROP TABLE IF EXISTS users");
$pdo->exec("SET FOREIGN_KEY_CHECKS=1");
echo "Existing tables dropped (if any).\n";
echo "Existing tables dropped.\n";
// 3. Create tables with correct columns
// 3. Create tables
// Users Table
$createUsersTable = "
CREATE TABLE users (
$pdo->exec(" CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role VARCHAR(20) DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)";
$pdo->exec($createUsersTable);
)");
echo "Users table ready.\n";
// Cars Table
// Added image_url to schema to maintain application compatibility
$createCarsTable = "
CREATE TABLE cars (
$pdo->exec(" CREATE TABLE cars (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
user_id INT NULL,
title VARCHAR(255) NULL,
make VARCHAR(100),
model VARCHAR(100),
year INT,
@ -62,29 +49,62 @@ try {
province VARCHAR(100),
city VARCHAR(100),
image_url VARCHAR(255) DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)";
$pdo->exec($createCarsTable);
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
)");
echo "Cars table ready.\n";
// 4. Add default admin user
$adminUsername = 'admin';
$adminPassword = '123';
$adminHash = password_hash($adminPassword, PASSWORD_DEFAULT);
$adminRole = 'admin';
// Bookings Table
$pdo->exec(" CREATE TABLE bookings (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
car_id INT NOT NULL,
booking_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(50) NOT NULL DEFAULT 'pending',
bank_province VARCHAR(100) NULL,
bank_account_number VARCHAR(100) NULL,
sale_price DECIMAL(10, 2) NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (car_id) REFERENCES cars(id) ON DELETE CASCADE
)");
echo "Bookings table ready.\n";
// Idempotent check handled by DROP TABLE above, but logic included for completeness if table wasn't dropped
// Reviews Table
$pdo->exec(" CREATE TABLE reviews (
id INT AUTO_INCREMENT PRIMARY KEY,
car_id INT NOT NULL,
user_id INT NOT NULL,
rating INT NOT NULL CHECK (rating >= 1 AND rating <= 5),
review TEXT,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (car_id) REFERENCES cars(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)");
echo "Reviews table ready.\n";
// 4. Add default admin user
$adminUsername = 'admin@gmail.com';
$adminPassword = '12345678';
$adminHash = password_hash($adminPassword, PASSWORD_DEFAULT);
// Check if admin exists (though we dropped tables, good practice)
$stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE username = ?");
$stmt->execute([$adminUsername]);
if ($stmt->fetchColumn() == 0) {
$insertAdmin = $pdo->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, ?)");
$insertAdmin->execute([$adminUsername, $adminHash, $adminRole]);
$insertAdmin = $pdo->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, 'admin')");
$insertAdmin->execute([$adminUsername, $adminHash]);
$adminId = $pdo->lastInsertId();
echo "Admin user created (User: $adminUsername, Pass: $adminPassword).\n";
} else {
echo "Admin user already exists.\n";
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
$stmt->execute([$adminUsername]);
$adminId = $stmt->fetchColumn();
}
// 5. Insert sample data into cars table
// 5. Insert sample data (15 Cars)
$carsData = [
[
'title' => 'Toyota Corolla 2020 Clean',
@ -93,12 +113,12 @@ try {
'year' => 2020,
'mileage' => 15000,
'price' => 18500.00,
'description' => 'Very clean car, no accidents.',
'description' => 'Very clean car, no accidents. Perfect for city driving.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'assets/images/placeholder_car1.jpg'
'image_url' => 'https://images.pexels.com/photos/112460/pexels-photo-112460.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Honda Civic 2018 Sport',
@ -107,32 +127,201 @@ try {
'year' => 2018,
'mileage' => 45000,
'price' => 16200.00,
'description' => 'Sport mode, leather seats.',
'description' => 'Sport mode, leather seats, sunroof. Excellent condition.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Herat',
'city' => 'Herat',
'image_url' => 'assets/images/placeholder_car2.jpg'
'image_url' => 'https://images.pexels.com/photos/170811/pexels-photo-170811.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Ford Ranger 2019',
'title' => 'Ford Ranger 2019 4x4',
'make' => 'Ford',
'model' => 'Ranger',
'year' => 2019,
'mileage' => 30000,
'price' => 25000.00,
'description' => 'Strong pickup for tough roads.',
'description' => 'Strong pickup for tough roads. 4x4 capability.',
'status' => 'pending',
'color' => 'Blue',
'province' => 'Kandahar',
'city' => 'Kandahar',
'image_url' => 'assets/images/placeholder_car3.jpg'
'image_url' => 'https://images.pexels.com/photos/919073/pexels-photo-919073.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Toyota Land Cruiser 2022',
'make' => 'Toyota',
'model' => 'Land Cruiser',
'year' => 2022,
'mileage' => 5000,
'price' => 85000.00,
'description' => 'Luxury SUV, fully loaded, V8 engine.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/205740/pexels-photo-205740.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Toyota Camry 2015 XLE',
'make' => 'Toyota',
'model' => 'Camry',
'year' => 2015,
'mileage' => 80000,
'price' => 12500.00,
'description' => 'Reliable family sedan, fuel efficient.',
'status' => 'approved',
'color' => 'Silver',
'province' => 'Mazar-i-Sharif',
'city' => 'Mazar',
'image_url' => 'https://images.pexels.com/photos/244206/pexels-photo-244206.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Hyundai Sonata 2021 Hybrid',
'make' => 'Hyundai',
'model' => 'Sonata',
'year' => 2021,
'mileage' => 12000,
'price' => 22000.00,
'description' => 'Hybrid engine, great mileage, modern tech.',
'status' => 'approved',
'color' => 'Grey',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/3764984/pexels-photo-3764984.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Mercedes-Benz C-Class 2016',
'make' => 'Mercedes-Benz',
'model' => 'C-Class',
'year' => 2016,
'mileage' => 55000,
'price' => 28000.00,
'description' => 'Luxury interior, smooth ride, imported from Germany.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Herat',
'city' => 'Herat',
'image_url' => 'https://images.pexels.com/photos/116675/pexels-photo-116675.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'BMW X5 2019 xDrive',
'make' => 'BMW',
'model' => 'X5',
'year' => 2019,
'mileage' => 25000,
'price' => 55000.00,
'description' => 'Premium SUV, panoramic sunroof, leather interior.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/3752169/pexels-photo-3752169.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Kia Sportage 2020 AWD',
'make' => 'Kia',
'model' => 'Sportage',
'year' => 2020,
'mileage' => 18000,
'price' => 21000.00,
'description' => 'Compact SUV, AWD, apple carplay.',
'status' => 'approved',
'color' => 'Red',
'province' => 'Jalalabad',
'city' => 'Jalalabad',
'image_url' => 'https://images.pexels.com/photos/4062468/pexels-photo-4062468.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Nissan Sunny 2017',
'make' => 'Nissan',
'model' => 'Sunny',
'year' => 2017,
'mileage' => 60000,
'price' => 9500.00,
'description' => 'Economic car, cheap maintenance.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kandahar',
'city' => 'Kandahar',
'image_url' => 'https://images.pexels.com/photos/4574184/pexels-photo-4574184.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Toyota Hilux 2021 Revo',
'make' => 'Toyota',
'model' => 'Hilux',
'year' => 2021,
'mileage' => 10000,
'price' => 42000.00,
'description' => 'Powerful diesel engine, off-road ready.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/6301931/pexels-photo-6301931.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Lexus LX570 2018',
'make' => 'Lexus',
'model' => 'LX570',
'year' => 2018,
'mileage' => 40000,
'price' => 95000.00,
'description' => 'Top of the line luxury, armored option available.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/1592384/pexels-photo-1592384.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Suzuki Alto 2022',
'make' => 'Suzuki',
'model' => 'Alto',
'year' => 2022,
'mileage' => 5000,
'price' => 7500.00,
'description' => 'Small city car, very fuel efficient.',
'status' => 'approved',
'color' => 'Red',
'province' => 'Mazar-i-Sharif',
'city' => 'Mazar',
'image_url' => 'https://images.pexels.com/photos/35967/mini-cooper-auto-model-vehicle.jpg?auto=compress&cs=tinysrgb&w=600' // Placeholder for small car
],
[
'title' => 'Mazda 6 2019',
'make' => 'Mazda',
'model' => '6',
'year' => 2019,
'mileage' => 28000,
'price' => 19500.00,
'description' => 'Stylish sedan, premium interior.',
'status' => 'approved',
'color' => 'Blue',
'province' => 'Herat',
'city' => 'Herat',
'image_url' => 'https://images.pexels.com/photos/1007410/pexels-photo-1007410.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Chevrolet Tahoe 2015',
'make' => 'Chevrolet',
'model' => 'Tahoe',
'year' => 2015,
'mileage' => 85000,
'price' => 26000.00,
'description' => 'Large family SUV, spacious, American muscle.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/4173163/pexels-photo-4173163.jpeg?auto=compress&cs=tinysrgb&w=600'
]
];
$insertCar = $pdo->prepare("INSERT INTO cars (title, make, model, year, mileage, price, description, status, color, province, city, image_url) VALUES (:title, :make, :model, :year, :mileage, :price, :description, :status, :color, :province, :city, :image_url)");
$insertCar = $pdo->prepare("INSERT INTO cars (user_id, title, make, model, year, mileage, price, description, status, color, province, city, image_url) VALUES (:user_id, :title, :make, :model, :year, :mileage, :price, :description, :status, :color, :province, :city, :image_url)");
foreach ($carsData as $car) {
$car['user_id'] = $adminId;
$insertCar->execute($car);
}
echo "Seed data inserted (" . count($carsData) . " cars).\n";
@ -141,5 +330,4 @@ try {
} catch (PDOException $e) {
die("Setup failed: " . $e->getMessage() . "\n");
}
}