sad
This commit is contained in:
parent
9c65e16259
commit
73e14b3353
68
README.md
68
README.md
@ -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]
|
||||
|
||||
@ -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>
|
||||
124
admin/index.php
124
admin/index.php
@ -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>
|
||||
|
||||
@ -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
91
admin/requests.php
Normal 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']) ?> • <?= 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>
|
||||
371
car_detail.php
371
car_detail.php
@ -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>
|
||||
|
||||
47
car_list.php
47
car_list.php
@ -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>
|
||||
7
db/migrations/005_add_user_id_and_purchase_fields.sql
Normal file
7
db/migrations/005_add_user_id_and_purchase_fields.sql
Normal 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;
|
||||
66
index.php
66
index.php
@ -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']); ?> • <?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']); ?> • <?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>
|
||||
|
||||
52
login.php
52
login.php
@ -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
44
partials/header.php
Normal 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
57
purchase_process.php
Normal 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
111
receipt.php
Normal 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
159
sell_car.php
Normal 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>
|
||||
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user