Compare commits

...

6 Commits

Author SHA1 Message Date
Flatlogic Bot
1c03905469 sad 2026-01-22 08:07:11 +00:00
Flatlogic Bot
cdb0630e5e sad 2026-01-22 07:43:24 +00:00
Flatlogic Bot
73e14b3353 sad 2026-01-21 17:27:41 +00:00
Flatlogic Bot
9c65e16259 sad 2026-01-21 14:05:59 +00:00
Flatlogic Bot
9643b213d0 sad 2026-01-21 08:43:40 +00:00
Flatlogic Bot
f054121ef2 26 2026-01-21 06:58:38 +00:00
37 changed files with 3870 additions and 519 deletions

61
README.md Normal file
View File

@ -0,0 +1,61 @@
# Car Sells in Afghanistan
A modern, responsive car marketplace web application.
## Features
* **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.
## Local Setup Instructions (XAMPP/LAMP)
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.
## Credentials
* **Admin User:**
* Username: `admin`
* Password: `123` (or `12345678` if updated manually)
## Usage Guide
### 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.
### 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]

139
about.php Normal file
View File

@ -0,0 +1,139 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once 'db/config.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About Us - Car Sells in Afghanistan</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
<link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
<style>
.about-hero {
background: linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('https://images.pexels.com/photos/116675/pexels-photo-116675.jpeg?auto=compress&cs=tinysrgb&w=1200');
background-size: cover;
background-position: center;
height: 400px;
display: flex;
align-items: center;
justify-content: center;
color: white;
text-align: center;
margin-bottom: 5rem;
}
.story-img {
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
}
.feature-card {
border: none;
border-radius: 20px;
transition: transform 0.3s ease;
height: 100%;
}
.feature-card:hover {
transform: translateY(-10px);
}
.feature-icon {
width: 60px;
height: 60px;
background: var(--primary-color);
color: white;
border-radius: 15px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
margin-bottom: 1.5rem;
}
</style>
</head>
<body>
<?php include 'partials/navbar.php'; ?>
<section class="about-hero">
<div class="container">
<h1 class="display-3 fw-bold mb-3">Our Story</h1>
<p class="lead">Building Trust in the Afghan Automotive Market</p>
</div>
</section>
<div class="container mb-5">
<div class="row align-items-center g-5">
<div class="col-lg-6">
<img src="https://images.pexels.com/photos/3184418/pexels-photo-3184418.jpeg?auto=compress&cs=tinysrgb&w=800" alt="About Me" class="img-fluid story-img">
</div>
<div class="col-lg-6">
<h2 class="display-5 fw-bold mb-4">A Story About Me & My Vision</h2>
<p class="lead text-muted mb-4">Hi, I'm the founder of <strong>Car Sells in Afghanistan</strong>. Growing up in Kabul, I always noticed the challenges people faced when trying to find reliable vehicles at fair prices.</p>
<p class="mb-4">My journey started with a simple idea: to create a transparent, digital platform that connects sellers and buyers across all provinces—from the bustling streets of Herat to the historic roads of Kandahar and Mazar-i-Sharif.</p>
<p class="mb-4">We aren't just selling cars; we are building a community based on trust. Every car listed on our platform undergoes a rigorous check to ensure it meets our quality standards. Our mission is to modernize the Afghan car market, one vehicle at a time.</p>
<div class="d-flex gap-3">
<div class="text-center">
<h4 class="fw-bold text-primary mb-0">10+</h4>
<small class="text-muted">Provinces</small>
</div>
<div class="vr"></div>
<div class="text-center">
<h4 class="fw-bold text-primary mb-0">500+</h4>
<small class="text-muted">Happy Clients</small>
</div>
<div class="vr"></div>
<div class="text-center">
<h4 class="fw-bold text-primary mb-0">1000+</h4>
<small class="text-muted">Cars Sold</small>
</div>
</div>
</div>
</div>
</div>
<section class="bg-light py-5">
<div class="container py-5">
<div class="text-center mb-5">
<h2 class="fw-bold">Why Choose Us?</h2>
<p class="text-muted">We provide the best service in the country</p>
</div>
<div class="row g-4">
<div class="col-md-4">
<div class="card feature-card p-4">
<div class="feature-icon">
<i class="bi bi-shield-check"></i>
</div>
<h4 class="fw-bold">Verified Cars</h4>
<p class="text-muted">Every car is inspected for mechanical issues and documentation authenticity before being listed.</p>
</div>
</div>
<div class="col-md-4">
<div class="card feature-card p-4">
<div class="feature-icon bg-success">
<i class="bi bi-cash-stack"></i>
</div>
<h4 class="fw-bold">Best Prices</h4>
<p class="text-muted">We ensure that our prices are competitive and reflect the true value of the vehicle in the Afghan market.</p>
</div>
</div>
<div class="col-md-4">
<div class="card feature-card p-4">
<div class="feature-icon bg-info">
<i class="bi bi-headset"></i>
</div>
<h4 class="fw-bold">24/7 Support</h4>
<p class="text-muted">Our dedicated team is always ready to assist you with your inquiries, whether you're buying or selling.</p>
</div>
</div>
</div>
</div>
</section>
<?php include 'partials/footer.php'; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

83
admin/bookings.php Normal file
View File

@ -0,0 +1,83 @@
<?php
session_start();
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: ../login.php");
exit();
}
require_once '../db/config.php';
$pdo = db();
// Fetch sales with user and car details
$bookings = $pdo->query("
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 = 'Sales History';
?>
<!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">Sales History</h1>
<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>
</body>
</html>

222
admin/cars.php Normal file
View File

@ -0,0 +1,222 @@
<?php
session_start();
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: ../login.php");
exit();
}
require_once '../db/config.php';
$pdo = db();
// Handle Add/Edit/Delete
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$carId = filter_input(INPUT_POST, 'car_id', FILTER_VALIDATE_INT);
if (isset($_POST['delete_car'])) {
$stmt = $pdo->prepare("DELETE FROM cars WHERE id = ?");
$stmt->execute([$carId]);
header("Location: cars.php");
exit();
}
// Sanitize and validate inputs
$make = trim(filter_input(INPUT_POST, 'make', FILTER_SANITIZE_SPECIAL_CHARS));
$model = trim(filter_input(INPUT_POST, 'model', FILTER_SANITIZE_SPECIAL_CHARS));
$year = filter_input(INPUT_POST, 'year', FILTER_VALIDATE_INT);
$price = filter_input(INPUT_POST, 'price', FILTER_VALIDATE_FLOAT);
$status = in_array($_POST['status'], ['approved', 'pending', 'sold', 'reserved']) ? $_POST['status'] : 'pending';
$description = trim(filter_input(INPUT_POST, 'description', FILTER_SANITIZE_SPECIAL_CHARS));
$color = trim(filter_input(INPUT_POST, 'color', FILTER_SANITIZE_SPECIAL_CHARS));
$mileage = filter_input(INPUT_POST, 'mileage', FILTER_VALIDATE_INT);
$imageUrl = $_POST['existing_image']; // Keep existing image by default
if (isset($_FILES['image']) && $_FILES['image']['error'] == 0) {
$targetDir = "../assets/images/cars/";
if (!is_dir($targetDir)) mkdir($targetDir, 0775, true);
$fileName = uniqid() . '-' . basename($_FILES["image"]["name"]);
$targetFilePath = $targetDir . $fileName;
if (move_uploaded_file($_FILES["image"]["tmp_name"], $targetFilePath)) {
$imageUrl = 'assets/images/cars/' . $fileName;
}
}
if ($carId) { // Update
$sql = "UPDATE cars SET make=?, model=?, year=?, price=?, status=?, description=?, image_url=?, color=?, mileage=? WHERE id=?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$make, $model, $year, $price, $status, $description, $imageUrl, $color, $mileage, $carId]);
} else { // Insert
$sql = "INSERT INTO cars (make, model, year, price, status, description, image_url, color, mileage) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$make, $model, $year, $price, $status, $description, $imageUrl, $color, $mileage]);
}
header("Location: cars.php");
exit();
}
// Fetching cars with filters
$search = $_GET['search'] ?? '';
$filter_status = $_GET['status'] ?? 'all';
$sql = "SELECT * FROM cars";
$params = [];
$where = [];
if (!empty($search)) {
$where[] = "(make LIKE ? OR model LIKE ?)";
$params[] = "%$search%";
$params[] = "%$search%";
}
if ($filter_status !== 'all') {
$where[] = "status = ?";
$params[] = $filter_status;
}
if (!empty($where)) {
$sql .= " WHERE " . implode(' AND ', $where);
}
$sql .= " ORDER BY created_at DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$cars = $stmt->fetchAll(PDO::FETCH_ASSOC);
$projectName = 'Manage Cars';
?>
<!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">
<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">
<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 Cars</h1>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#carModal"><i class="bi bi-plus-circle me-2"></i>Add New Car</button>
</div>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr><th>Image</th><th>Make & Model</th><th>Price</th><th>Status</th><th>Year</th><th>Actions</th></tr>
</thead>
<tbody>
<?php foreach ($cars as $car): ?>
<tr>
<td><img src="../<?= htmlspecialchars($car['image_url']) ?>" height="50" width="80" style="object-fit: cover;" class="rounded" alt="<?= htmlspecialchars($car['make']) ?>"></td>
<td><b><?= htmlspecialchars($car['make'] . ' ' . $car['model']) ?></b></td>
<td>$<?= number_format($car['price']) ?></td>
<td><span class="badge bg-<?= str_replace(['approved', 'pending','reserved', 'sold'], ['success', 'warning','info', 'danger'], $car['status']) ?>"><?= htmlspecialchars(ucfirst($car['status'])) ?></span></td>
<td><?= htmlspecialchars($car['year']) ?></td>
<td>
<button type="button" class="btn btn-sm btn-outline-primary edit-btn" data-bs-toggle="modal" data-bs-target="#carModal" data-car='<?= htmlspecialchars(json_encode($car), ENT_QUOTES, 'UTF-8') ?>'><i class="bi bi-pencil-square"></i></button>
<form method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this car?');">
<input type="hidden" name="car_id" value="<?= $car['id'] ?>">
<button type="submit" name="delete_car" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Car Modal -->
<div class="modal fade" id="carModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="car_id" id="car_id">
<input type="hidden" name="existing_image" id="existing_image">
<div class="modal-header">
<h5 class="modal-title" id="modalTitle">Add Car</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="row g-3">
<div class="col-md-6"><label class="form-label">Make</label><input type="text" name="make" id="make" class="form-control" required></div>
<div class="col-md-6"><label class="form-label">Model</label><input type="text" name="model" id="model" class="form-control" required></div>
<div class="col-md-6"><label class="form-label">Year</label><input type="number" name="year" id="year" class="form-control" required></div>
<div class="col-md-6"><label class="form-label">Price</label><input type="number" name="price" id="price" class="form-control" step="100" required></div>
<div class="col-md-6"><label class="form-label">Mileage (km)</label><input type="number" name="mileage" id="mileage" class="form-control"></div>
<div class="col-md-6"><label class="form-label">Color</label><input type="text" name="color" id="color" class="form-control"></div>
<div class="col-md-6">
<label class="form-label">Status</label>
<select name="status" id="status" class="form-select">
<option value="pending">Pending</option>
<option value="approved">Approved</option>
<option value="reserved">Reserved</option>
<option value="sold">Sold</option>
</select>
</div>
<div class="col-12"><label class="form-label">Description</label><textarea name="description" id="description" class="form-control" rows="3"></textarea></div>
<div class="col-12">
<label class="form-label">Image</label>
<input type="file" name="image" class="form-control">
<img id="image_preview" src="" class="img-fluid rounded mt-2" style="max-height: 150px; display: none;">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save Car</button>
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const carModal = document.getElementById('carModal');
carModal.addEventListener('show.bs.modal', function (event) {
const button = event.relatedTarget;
const modalTitle = carModal.querySelector('.modal-title');
const form = carModal.querySelector('form');
const imagePreview = document.getElementById('image_preview');
form.reset();
imagePreview.style.display = 'none';
imagePreview.src = '';
const carData = button.dataset.car ? JSON.parse(button.dataset.car) : null;
if (carData) {
modalTitle.textContent = 'Edit Car: ' + carData.make + ' ' + carData.model;
form.querySelector('#car_id').value = carData.id;
form.querySelector('#make').value = carData.make;
form.querySelector('#model').value = carData.model;
form.querySelector('#year').value = carData.year;
form.querySelector('#price').value = carData.price;
form.querySelector('#status').value = carData.status;
form.querySelector('#description').value = carData.description;
form.querySelector('#mileage').value = carData.mileage;
form.querySelector('#color').value = carData.color;
form.querySelector('#existing_image').value = carData.image_url;
if (carData.image_url) {
imagePreview.src = '../' + carData.image_url;
imagePreview.style.display = 'block';
}
} else {
modalTitle.textContent = 'Add New Car';
form.querySelector('#car_id').value = '';
}
});
});
</script>
</body>
</html>

139
admin/index.php Normal file
View File

@ -0,0 +1,139 @@
<?php
session_start();
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: ../login.php");
exit();
}
require_once '../db/config.php';
$pdo = db();
// Stats
$stats = [
'users' => $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn(),
'cars' => $pdo->query("SELECT COUNT(*) FROM cars")->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, 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: 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';
?>
<!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">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?= time() ?>">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<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">Dashboard</h1>
<span class="text-muted">Welcome, <?= htmlspecialchars($_SESSION['username']) ?>!</span>
</div>
<!-- Stat Cards -->
<div class="row g-4 mb-4">
<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 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 border-0 shadow-sm">
<div class="card-body">
<h5 class="card-title">Inventory Status</h5>
<div style="height: 250px;">
<canvas id="statusChart"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
// 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: 'Revenue ($)',
data: <?= json_encode(array_column($sales_data, 'total')) ?>,
borderColor: '#198754',
backgroundColor: 'rgba(25, 135, 84, 0.1)',
fill: true,
tension: 0.3
}]
},
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>
</body>
</html>

View File

@ -0,0 +1,83 @@
<?php
// This is a new file: admin/partials/sidebar.php
$current_page = basename($_SERVER['PHP_SELF']);
?>
<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' : '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 == '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 == '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 == '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 == '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 pt-3 border-top">
<a href="../index.php" class="d-flex align-items-center link-dark text-decoration-none mb-3">
<i class="bi bi-house-door me-2"></i> <strong>Back to Website</strong>
</a>
<a href="../logout.php" class="d-flex align-items-center link-danger 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>

116
admin/reviews.php Normal file
View File

@ -0,0 +1,116 @@
<?php
session_start();
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: ../login.php");
exit();
}
require_once '../db/config.php';
$pdo = db();
// Handle review status change or deletion
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$reviewId = filter_input(INPUT_POST, 'review_id', FILTER_VALIDATE_INT);
if ($reviewId) {
if (isset($_POST['approve'])) {
$pdo->prepare("UPDATE reviews SET status = 'approved' WHERE id = ?")->execute([$reviewId]);
} elseif (isset($_POST['unapprove'])) {
$pdo->prepare("UPDATE reviews SET status = 'pending' WHERE id = ?")->execute([$reviewId]);
} elseif (isset($_POST['delete'])) {
$pdo->prepare("DELETE FROM reviews WHERE id = ?")->execute([$reviewId]);
}
}
header("Location: reviews.php");
exit();
}
// Fetch reviews with user and car details
$reviews = $pdo->query("
SELECT r.id, r.rating, r.review, r.status, r.created_at, u.username, c.make, c.model
FROM reviews r
JOIN users u ON r.user_id = u.id
JOIN cars c ON r.car_id = c.id
ORDER BY r.created_at DESC
")->fetchAll(PDO::FETCH_ASSOC);
$projectName = 'Manage Reviews';
function render_stars($rating) {
$rating = (int)$rating;
$html = '';
for ($i = 1; $i <= 5; $i++) {
$html .= $i <= $rating ? '★' : '☆';
}
return '<span class="text-warning">' . $html . '</span>';
}
?>
<!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">
<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">
<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 Reviews</h1>
</div>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr><th>Reviewer</th><th>Car</th><th>Review</th><th>Status</th><th>Actions</th></tr>
</thead>
<tbody>
<?php if (empty($reviews)): ?>
<tr><td colspan="5" class="text-center">No reviews found.</td></tr>
<?php endif; ?>
<?php foreach ($reviews as $review): ?>
<tr>
<td><b><?= htmlspecialchars($review['username']) ?></b></td>
<td><?= htmlspecialchars($review['make'] . ' ' . $review['model']) ?></td>
<td>
<div><?= render_stars($review['rating']) ?></div>
<small class="text-muted">"<?= nl2br(htmlspecialchars($review['review'])) ?>"</small>
</td>
<td>
<span class="badge rounded-pill bg-<?= $review['status'] === 'approved' ? 'success' : 'warning' ?>">
<?= htmlspecialchars(ucfirst($review['status'])) ?>
</span>
</td>
<td>
<form method="POST" class="d-inline-flex gap-2">
<input type="hidden" name="review_id" value="<?= $review['id'] ?>">
<?php if ($review['status'] === 'pending'): ?>
<button type="submit" name="approve" class="btn btn-sm btn-success" title="Approve"><i class="bi bi-check-lg"></i></button>
<?php else: ?>
<button type="submit" name="unapprove" class="btn btn-sm btn-secondary" title="Set as Pending"><i class="bi bi-clock-history"></i></button>
<?php endif; ?>
<button type="submit" name="delete" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to delete this review?');" title="Delete"><i class="bi bi-trash"></i></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

115
admin/users.php Normal file
View File

@ -0,0 +1,115 @@
<?php
session_start();
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: ../login.php");
exit();
}
require_once '../db/config.php';
$pdo = db();
// Handle user actions (delete)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$userId = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT);
if ($userId && $userId != $_SESSION['user_id']) { // Prevent admin from deleting themselves
if (isset($_POST['delete_user'])) {
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$userId]);
}
}
header("Location: users.php");
exit();
}
// Fetch users with filters
$search = $_GET['search'] ?? '';
// Schema: id, username, password, role, created_at
$sql = "SELECT id, username, role, created_at FROM users";
$params = [];
$where = [];
if (!empty($search)) {
$where[] = "(username LIKE ?)";
$params[] = "%$search%";
}
if (!empty($where)) {
$sql .= " WHERE " . implode(' AND ', $where);
}
$sql .= " ORDER BY created_at DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
$projectName = 'Manage Users';
?>
<!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">
<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">
<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 Users</h1>
</div>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr><th>User</th><th>Role</th><th>Joined</th><th>Actions</th></tr>
</thead>
<tbody>
<?php if (empty($users)): ?>
<tr><td colspan="4" class="text-center">No users found.</td></tr>
<?php endif; ?>
<?php foreach ($users as $user): ?>
<tr>
<td>
<div class="d-flex align-items-center">
<img src="https://i.pravatar.cc/40?u=<?= htmlspecialchars($user['username']) ?>" class="rounded-circle me-3" alt="<?= htmlspecialchars($user['username']) ?>">
<div>
<b><?= htmlspecialchars($user['username']) ?></b>
</div>
</div>
</td>
<td><span class="badge bg-<?= $user['role'] === 'admin' ? 'primary' : 'secondary' ?>"><?= htmlspecialchars(ucfirst($user['role'])) ?></span></td>
<td><?= date("M d, Y", strtotime($user['created_at'])) ?></td>
<td>
<?php if ($user['id'] != $_SESSION['user_id']): // Prevent admin from editing themselves ?>
<form method="POST" class="d-inline-flex gap-2">
<input type="hidden" name="user_id" value="<?= $user['id'] ?>">
<button type="submit" name="delete_user" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this user?');" title="Delete User">
<i class="bi bi-trash"></i>
</button>
</form>
<?php else: ?>
<span class="text-muted fst-italic">Current User</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,346 +1,277 @@
/* assets/css/custom.css */
/*
* MODERN, YOUTHFUL & INVITING AESTHETIC
*
* - Light, neutral base theme with vibrant accent colors.
* - Clear, large typography for a strong visual hierarchy.
* - Comfortably large, prominent interactive elements.
* - Tasteful soft gradients and subtle shadows.
*/
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Playfair+Display:wght@700&display=swap');
:root {
--color-bg: #ffffff;
--color-text: #1a1a1a;
--color-primary: #2563EB; /* Vibrant Blue */
--color-secondary: #000000;
--color-accent: #A3E635; /* Lime Green */
--color-surface: #f8f9fa;
--font-heading: 'Space Grotesk', sans-serif;
--font-body: 'Inter', sans-serif;
--border-width: 2px;
--shadow-hard: 5px 5px 0px #000;
--shadow-hover: 8px 8px 0px #000;
--radius-pill: 50rem;
--radius-card: 1rem;
/* Color Palette */
--primary-color: #4f46e5; /* Vibrant Indigo */
--secondary-color: #10b981; /* Bright Emerald */
--accent-color: #ec4899; /* Hot Pink */
--bg-color: #f9fafb; /* Very Light Gray */
--surface-color: #ffffff; /* White */
--text-color: #1f2937; /* Dark Gray */
--text-muted: #6b7280; /* Medium Gray */
--border-color: #e5e7eb; /* Light Gray */
/* Typography */
--font-primary: 'Poppins', sans-serif;
--font-display: 'Playfair Display', serif;
/* Effects */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--border-radius: 0.75rem; /* 12px */
}
/* Base & Typography */
body {
font-family: var(--font-body);
background-color: var(--color-bg);
color: var(--color-text);
overflow-x: hidden;
font-family: var(--font-primary);
background-color: var(--bg-color);
color: var(--text-color);
line-height: 1.6;
}
h1, h2, h3, h4, h5, h6, .navbar-brand {
font-family: var(--font-heading);
letter-spacing: -0.03em;
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-display);
color: var(--primary-color);
font-weight: 700;
}
/* Utilities */
.text-primary { color: var(--color-primary) !important; }
.bg-black { background-color: #000 !important; }
.text-white { color: #fff !important; }
.shadow-hard { box-shadow: var(--shadow-hard); }
.border-2-black { border: var(--border-width) solid #000; }
.py-section { padding-top: 5rem; padding-bottom: 5rem; }
h1 { font-size: 3rem; }
h2 { font-size: 2.25rem; }
h3 { font-size: 1.875rem; }
a {
color: var(--primary-color);
text-decoration: none;
transition: color 0.3s ease;
}
a:hover {
color: var(--accent-color);
}
/* Layout & Containers */
.container {
max-width: 1200px;
}
.section-padding {
padding: 6rem 0;
}
/* Navbar */
.navbar {
background: rgba(255, 255, 255, 0.9);
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-bottom: var(--border-width) solid transparent;
transition: all 0.3s;
padding-top: 1rem;
padding-bottom: 1rem;
-webkit-backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border-color);
box-shadow: var(--shadow-sm);
padding: 1rem 0;
}
.navbar.scrolled {
border-bottom-color: #000;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.brand-text {
font-size: 1.5rem;
font-weight: 800;
.navbar-brand {
font-family: var(--font-display);
font-weight: 700;
font-size: 1.75rem;
color: var(--primary-color) !important;
}
.nav-link {
font-weight: 500;
color: var(--color-text);
margin-left: 1rem;
position: relative;
font-weight: 600;
color: var(--text-muted) !important;
padding: 0.5rem 1rem !important;
border-radius: var(--border-radius);
transition: all 0.3s ease;
}
.nav-link:hover, .nav-link.active {
color: var(--color-primary);
color: var(--accent-color) !important;
background-color: rgba(236, 72, 153, 0.1);
}
/* Buttons */
.btn {
font-weight: 700;
font-family: var(--font-heading);
font-weight: 600;
padding: 0.8rem 2rem;
border-radius: var(--radius-pill);
border: var(--border-width) solid #000;
transition: all 0.2s cubic-bezier(0.25, 1, 0.5, 1);
box-shadow: var(--shadow-hard);
}
.btn:hover {
transform: translate(-2px, -2px);
box-shadow: var(--shadow-hover);
}
.btn:active {
transform: translate(2px, 2px);
box-shadow: 0 0 0 #000;
border-radius: var(--border-radius);
border: none;
transition: all 0.3s ease;
box-shadow: var(--shadow-md);
}
.btn-primary {
background-color: var(--color-primary);
border-color: #000;
color: #fff;
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
color: white;
}
.btn-primary:hover {
background-color: #1d4ed8;
border-color: #000;
color: #fff;
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.btn-outline-dark {
background-color: #fff;
color: #000;
.btn-secondary {
background-color: var(--surface-color);
color: var(--primary-color);
border: 1px solid var(--border-color);
}
.btn-cta {
background-color: var(--color-accent);
color: #000;
.btn-secondary:hover {
background-color: var(--bg-color);
}
.btn-cta:hover {
background-color: #8cc629;
color: #000;
}
/* Hero Section */
.hero-section {
min-height: 100vh;
padding-top: 80px;
}
.background-blob {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.6;
z-index: 1;
}
.blob-1 {
top: -10%;
right: -10%;
width: 600px;
height: 600px;
background: radial-gradient(circle, var(--color-accent), transparent);
}
.blob-2 {
bottom: 10%;
left: -10%;
width: 500px;
height: 500px;
background: radial-gradient(circle, var(--color-primary), transparent);
}
.highlight-text {
background: linear-gradient(120deg, transparent 0%, transparent 40%, var(--color-accent) 40%, var(--color-accent) 100%);
background-repeat: no-repeat;
background-size: 100% 40%;
background-position: 0 88%;
padding: 0 5px;
}
.dot { color: var(--color-primary); }
.badge-pill {
display: inline-block;
padding: 0.5rem 1rem;
border: 2px solid #000;
border-radius: 50px;
font-weight: 700;
background: #fff;
box-shadow: 4px 4px 0 #000;
font-family: var(--font-heading);
font-size: 0.9rem;
}
/* Marquee */
.marquee-container {
overflow: hidden;
white-space: nowrap;
border-top: 2px solid #000;
border-bottom: 2px solid #000;
}
.rotate-divider {
transform: rotate(-2deg) scale(1.05);
z-index: 10;
position: relative;
margin-top: -50px;
margin-bottom: 30px;
}
.marquee-content {
display: inline-block;
animation: marquee 20s linear infinite;
font-family: var(--font-heading);
font-weight: 700;
font-size: 1.5rem;
letter-spacing: 2px;
}
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
/* Portfolio Cards */
.project-card {
border: 2px solid #000;
border-radius: var(--radius-card);
overflow: hidden;
background: #fff;
transition: transform 0.3s ease;
box-shadow: var(--shadow-hard);
height: 100%;
display: flex;
flex-direction: column;
}
.project-card:hover {
transform: translateY(-10px);
box-shadow: 8px 8px 0 #000;
}
.card-img-holder {
height: 250px;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 2px solid #000;
position: relative;
font-size: 4rem;
}
.placeholder-art {
transition: transform 0.3s ease;
}
.project-card:hover .placeholder-art {
transform: scale(1.2) rotate(10deg);
}
.bg-soft-blue { background-color: #e0f2fe; }
.bg-soft-green { background-color: #dcfce7; }
.bg-soft-purple { background-color: #f3e8ff; }
.bg-soft-yellow { background-color: #fef9c3; }
.category-tag {
position: absolute;
top: 15px;
right: 15px;
background: #000;
color: #fff;
padding: 5px 12px;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 700;
}
.card-body { padding: 1.5rem; }
.link-arrow {
text-decoration: none;
color: #000;
font-weight: 700;
display: inline-flex;
align-items: center;
margin-top: auto;
}
.link-arrow i { transition: transform 0.2s; margin-left: 5px; }
.link-arrow:hover i { transform: translateX(5px); }
/* About */
.about-image-stack {
position: relative;
height: 400px;
width: 100%;
}
.stack-card {
position: absolute;
width: 80%;
height: 100%;
border-radius: var(--radius-card);
border: 2px solid #000;
box-shadow: var(--shadow-hard);
left: 10%;
transform: rotate(-3deg);
background-size: cover;
}
/* Forms */
.form-control {
border: 2px solid #000;
border-radius: 0.5rem;
/* Forms & Inputs */
.form-control, .form-select {
padding: 1rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background-color: var(--surface-color);
transition: all 0.3s ease;
font-size: 1rem;
}
.form-control:focus, .form-select:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2);
}
.form-label {
font-weight: 600;
margin-bottom: 0.5rem;
}
/* Cards */
.card {
background-color: var(--surface-color);
border: none;
border-radius: var(--border-radius);
box-shadow: var(--shadow-md);
overflow: hidden;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
height: 100%;
}
.card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-lg);
}
.card-img-top {
object-fit: cover;
}
.card-body {
padding: 1.75rem;
}
.card-title {
font-family: var(--font-primary);
font-weight: 600;
font-size: 1.25rem;
}
/* Login/Register Form */
.form-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(-45deg, #e0c3fc, #8ec5fc);
}
.auth-card {
width: 100%;
max-width: 450px;
padding: 3rem;
}
/* Admin Dashboard */
.admin-wrapper {
display: flex;
}
.admin-sidebar {
width: 260px;
background: var(--primary-color);
color: white;
min-height: 100vh;
padding: 2rem 1rem;
position: fixed;
height: 100%;
}
.admin-sidebar .navbar-brand {
color: white !important;
}
.admin-sidebar .nav-link {
color: rgba(255, 255, 255, 0.7) !important;
font-weight: 500;
background: #f8f9fa;
}
.form-control:focus {
box-shadow: 4px 4px 0 var(--color-primary);
border-color: #000;
background: #fff;
.admin-sidebar .nav-link:hover, .admin-sidebar .nav-link.active {
color: white !important;
background-color: rgba(255, 255, 255, 0.1);
}
/* Animations */
.animate-up {
opacity: 0;
transform: translateY(30px);
animation: fadeUp 0.8s ease forwards;
.admin-main-content {
margin-left: 260px;
padding: 2rem;
width: calc(100% - 260px);
}
.delay-100 { animation-delay: 0.1s; }
.delay-200 { animation-delay: 0.2s; }
@keyframes fadeUp {
to {
opacity: 1;
transform: translateY(0);
}
.stat-card {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
padding: 2rem;
border-radius: var(--border-radius);
margin-bottom: 1.5rem;
}
/* Social */
.social-links a {
transition: transform 0.2s;
display: inline-block;
}
.social-links a:hover {
transform: scale(1.2) rotate(10deg);
color: var(--color-accent) !important;
.stat-card h4 {
color: white;
}
/* Responsive */
@media (max-width: 991px) {
.rotate-divider {
transform: rotate(0);
margin-top: 0;
margin-bottom: 2rem;
}
.hero-section {
padding-top: 120px;
text-align: center;
min-height: auto;
padding-bottom: 100px;
}
.display-1 { font-size: 3.5rem; }
.blob-1 { width: 300px; height: 300px; right: -20%; }
.blob-2 { width: 300px; height: 300px; left: -20%; }
/* Footer */
.footer {
background-color: var(--text-color);
color: var(--bg-color);
padding: 4rem 0 2rem;
margin-top: 4rem;
}
.footer a {
color: var(--secondary-color);
}
.footer a:hover {
color: var(--accent-color);
}
/* Carousel Tweaks */
.carousel-caption {
text-shadow: 2px 2px 4px rgba(0,0,0,0.6);
}
.object-fit-cover {
object-fit: cover;
}
/* Utilities */
.ls-1 {
letter-spacing: 1px;
}
.hover-lift:hover {
transform: translateY(-8px);
}

View File

@ -1,73 +1,29 @@
document.addEventListener('DOMContentLoaded', () => {
// Smooth scrolling for navigation links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
// Close mobile menu if open
const navbarToggler = document.querySelector('.navbar-toggler');
const navbarCollapse = document.querySelector('.navbar-collapse');
if (navbarCollapse.classList.contains('show')) {
navbarToggler.click();
}
const themeToggle = document.querySelector('.theme-toggle');
// Scroll with offset
const offset = 80;
const elementPosition = targetElement.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - offset;
if (themeToggle) {
const body = document.body;
const icon = themeToggle.querySelector('i');
window.scrollTo({
top: offsetPosition,
behavior: "smooth"
});
}
});
});
// Navbar scroll effect
const navbar = document.querySelector('.navbar');
window.addEventListener('scroll', () => {
if (window.scrollY > 50) {
navbar.classList.add('scrolled', 'shadow-sm', 'bg-white');
navbar.classList.remove('bg-transparent');
} else {
navbar.classList.remove('scrolled', 'shadow-sm', 'bg-white');
navbar.classList.add('bg-transparent');
const currentTheme = localStorage.getItem('theme');
if (currentTheme === 'dark') {
body.classList.add('dark-mode');
icon.classList.remove('bi-moon-fill');
icon.classList.add('bi-sun-fill');
}
});
// Intersection Observer for fade-up animations
const observerOptions = {
threshold: 0.1,
rootMargin: "0px 0px -50px 0px"
};
themeToggle.addEventListener('click', () => {
body.classList.toggle('dark-mode');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-up');
entry.target.style.opacity = "1";
observer.unobserve(entry.target); // Only animate once
if (body.classList.contains('dark-mode')) {
localStorage.setItem('theme', 'dark');
icon.classList.remove('bi-moon-fill');
icon.classList.add('bi-sun-fill');
} else {
localStorage.setItem('theme', 'light');
icon.classList.remove('bi-sun-fill');
icon.classList.add('bi-moon-fill');
}
});
}, observerOptions);
// Select elements to animate (add a class 'reveal' to them in HTML if not already handled by CSS animation)
// For now, let's just make sure the hero animations run.
// If we want scroll animations, we'd add opacity: 0 to elements in CSS and reveal them here.
// Given the request, the CSS animation I added runs on load for Hero.
// Let's make the project cards animate in.
const projectCards = document.querySelectorAll('.project-card');
projectCards.forEach((card, index) => {
card.style.opacity = "0";
card.style.animationDelay = `${index * 0.1}s`;
observer.observe(card);
});
}
});

289
car_detail.php Normal file
View File

@ -0,0 +1,289 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header("Location: car_list.php");
exit();
}
$carId = (int)$_GET['id'];
$pdo = db();
$message = '';
$message_type = '';
// Fetch car details
$stmt = $pdo->prepare("SELECT * FROM cars WHERE id = ?");
$stmt->execute([$carId]);
$car = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$car) {
header("Location: car_list.php");
exit();
}
// Fetch approved reviews
$stmt = $pdo->prepare("SELECT r.*, u.username FROM reviews r JOIN users u ON r.user_id = u.id WHERE r.car_id = ? AND r.status = 'approved' ORDER BY r.created_at DESC");
$stmt->execute([$carId]);
$reviews = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 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';
}
}
$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-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-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="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>
</div>
<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>
<?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 (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>
<?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>
<?php endif; ?>
</div>
</div>
</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>
</html>

146
car_list.php Normal file
View File

@ -0,0 +1,146 @@
<?php
require_once __DIR__ . '/db/config.php';
$pdo = db();
// Filtering Logic
$whereClauses = ["status IN ('approved', 'sold')"];
$params = [];
if (!empty($_GET['make'])) {
$whereClauses[] = "make = ?";
$params[] = $_GET['make'];
}
if (!empty($_GET['province'])) {
$whereClauses[] = "province = ?";
$params[] = $_GET['province'];
}
if (!empty($_GET['min_price'])) {
$whereClauses[] = "price >= ?";
$params[] = $_GET['min_price'];
}
if (!empty($_GET['max_price'])) {
$whereClauses[] = "price <= ?";
$params[] = $_GET['max_price'];
}
if (!empty($_GET['search'])) {
$whereClauses[] = "(model LIKE ? OR description LIKE ?)";
$searchTerm = "%{$_GET['search']}%";
$params[] = $searchTerm;
$params[] = $searchTerm;
}
$sql = "SELECT * FROM cars";
if (!empty($whereClauses)) {
$sql .= " WHERE " . implode(' AND ', $whereClauses);
}
$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 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);
$pageTitle = 'All Car Listings';
include 'partials/header.php';
?>
<header class="bg-light py-5">
<div class="container text-center">
<h1 class="display-4">Our Vehicle Collection</h1>
<p class="lead text-muted">Browse the complete inventory of our certified pre-owned vehicles from all over Afghanistan.</p>
</div>
</header>
<main class="container section-padding py-5">
<div class="row g-5">
<aside class="col-lg-3">
<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">
<label for="search" class="form-label">Keyword</label>
<input type="text" name="search" id="search" class="form-control" placeholder="e.g., Corolla, V8" value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
</div>
<div class="mb-3">
<label for="make" class="form-label">Make</label>
<select name="make" id="make" class="form-select">
<option value="">All Makes</option>
<?php foreach ($makes as $make): ?>
<option value="<?= htmlspecialchars($make) ?>" <?= (($_GET['make'] ?? '') === $make) ? 'selected' : '' ?>><?= htmlspecialchars($make) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label for="province" class="form-label">Province</label>
<select name="province" id="province" class="form-select">
<option value="">All Provinces</option>
<?php foreach ($provinces as $province): ?>
<option value="<?= htmlspecialchars($province) ?>" <?= (($_GET['province'] ?? '') === $province) ? 'selected' : '' ?>><?= htmlspecialchars($province) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Price Range ($)</label>
<div class="d-flex gap-2">
<input type="number" name="min_price" class="form-control" placeholder="Min" value="<?= htmlspecialchars($_GET['min_price'] ?? '') ?>">
<input type="number" name="max_price" class="form-control" placeholder="Max" value="<?= htmlspecialchars($_GET['max_price'] ?? '') ?>">
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Apply Filters</button>
<a href="car_list.php" class="btn btn-secondary">Reset Filters</a>
</div>
</form>
</div>
</aside>
<div class="col-lg-9">
<div class="row g-4">
<?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 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>
<div class="d-flex justify-content-between text-muted small mb-3">
<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(--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>
<?php endforeach; ?>
<?php else: ?>
<div class="col-12">
<div class="alert alert-warning text-center" role="alert">
<h4 class="alert-heading">No Cars Found!</h4>
<p>Your search did not match any of our vehicles. Try adjusting your filters or check back later.</p>
<hr>
<a href="car_list.php" class="btn btn-dark">Clear Filters and See All Cars</a>
</div>
</div>
<?php endif; ?>
</div>
</div>
</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>
</body>
</html>

158
contact.php Normal file
View File

@ -0,0 +1,158 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once 'db/config.php';
require_once 'mail/MailService.php';
$success_msg = '';
$error_msg = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$subject = $_POST['subject'] ?? 'Contact Form Inquiry';
$message = $_POST['message'] ?? '';
if (empty($name) || empty($email) || empty($message)) {
$error_msg = "Please fill in all required fields.";
} else {
$res = MailService::sendContactMessage($name, $email, $message, null, $subject);
if (!empty($res['success'])) {
$success_msg = "Thank you! Your message has been sent successfully. We will get back to you soon.";
} else {
$error_msg = "Sorry, there was an error sending your message. Please try again later. (Error: " . ($res['error'] ?? 'Unknown') . ")";
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Us - Car Sells in Afghanistan</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
<link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
<style>
.contact-info-card {
border: none;
border-radius: 20px;
background: var(--primary-color);
color: white;
padding: 3rem;
height: 100%;
}
.contact-form-card {
border: none;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.05);
padding: 3rem;
}
.form-control {
padding: 0.8rem 1.2rem;
border-radius: 10px;
border: 1px solid #eee;
}
.form-control:focus {
box-shadow: none;
border-color: var(--primary-color);
}
</style>
</head>
<body class="bg-light">
<?php include 'partials/navbar.php'; ?>
<div class="container py-5 my-5">
<div class="row g-5">
<div class="col-lg-5">
<div class="contact-info-card">
<h2 class="display-6 fw-bold mb-4">Contact Information</h2>
<p class="mb-5 opacity-75">Have questions about a car? Or want to list your own? Reach out to us through any of these channels.</p>
<div class="d-flex align-items-center mb-4">
<div class="fs-3 me-4"><i class="bi bi-geo-alt"></i></div>
<div>
<h5 class="mb-0 fw-bold">Address</h5>
<p class="mb-0 opacity-75">Shar-e-Naw, Kabul, Afghanistan</p>
</div>
</div>
<div class="d-flex align-items-center mb-4">
<div class="fs-3 me-4"><i class="bi bi-telephone"></i></div>
<div>
<h5 class="mb-0 fw-bold">Phone</h5>
<p class="mb-0 opacity-75">+93 700 123 456</p>
</div>
</div>
<div class="d-flex align-items-center mb-4">
<div class="fs-3 me-4"><i class="bi bi-envelope"></i></div>
<div>
<h5 class="mb-0 fw-bold">Email</h5>
<p class="mb-0 opacity-75">info@carsells.af</p>
</div>
</div>
<div class="mt-5">
<h5 class="fw-bold mb-3">Follow Us</h5>
<div class="d-flex gap-3">
<a href="#" class="text-white fs-4"><i class="bi bi-facebook"></i></a>
<a href="#" class="text-white fs-4"><i class="bi bi-instagram"></i></a>
<a href="#" class="text-white fs-4"><i class="bi bi-whatsapp"></i></a>
</div>
</div>
</div>
</div>
<div class="col-lg-7">
<div class="contact-form-card bg-white">
<h2 class="fw-bold mb-4">Send us a Message</h2>
<?php if ($success_msg): ?>
<div class="alert alert-success border-0 rounded-4 mb-4">
<i class="bi bi-check-circle-fill me-2"></i> <?= $success_msg ?>
</div>
<?php endif; ?>
<?php if ($error_msg): ?>
<div class="alert alert-danger border-0 rounded-4 mb-4">
<i class="bi bi-exclamation-triangle-fill me-2"></i> <?= $error_msg ?>
</div>
<?php endif; ?>
<form action="contact.php" method="POST">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label small fw-bold text-muted">Full Name</label>
<input type="text" name="name" class="form-control" placeholder="John Doe" required>
</div>
<div class="col-md-6">
<label class="form-label small fw-bold text-muted">Email Address</label>
<input type="email" name="email" class="form-control" placeholder="john@example.com" required>
</div>
<div class="col-12">
<label class="form-label small fw-bold text-muted">Subject</label>
<input type="text" name="subject" class="form-control" placeholder="How can we help?">
</div>
<div class="col-12">
<label class="form-label small fw-bold text-muted">Message</label>
<textarea name="message" class="form-control" rows="5" placeholder="Your message here..." required></textarea>
</div>
<div class="col-12 mt-4">
<button type="submit" class="btn btn-primary btn-lg rounded-pill px-5">Send Message</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<?php include 'partials/footer.php'; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

102
dashboard.php Normal file
View File

@ -0,0 +1,102 @@
<?php
session_start();
// Check if the user is logged in
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
require_once 'db/config.php';
$user = null;
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $_SESSION['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("Dashboard Page Error: " . $e->getMessage());
}
// If for some reason user is not found in DB, destroy session and redirect
if (!$user) {
session_destroy();
header("Location: login.php");
exit();
}
$projectName = 'User Dashboard';
?>
<!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); ?></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 href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<?php include 'partials/navbar.php'; ?>
<main class="container my-5">
<div class="row">
<!-- Sidebar -->
<div class="col-md-4">
<div class="card">
<div class="card-body text-center">
<i class="bi bi-person-circle display-3 text-primary"></i>
<h5 class="card-title mt-3"><?php echo htmlspecialchars($user['username']); ?></h5>
<p class="card-text text-muted"><?php echo htmlspecialchars($user['email']); ?></p>
<span class="badge bg-secondary"><?php echo ucfirst(htmlspecialchars($user['role'])); ?></span>
</div>
<div class="list-group list-group-flush">
<a href="dashboard.php" class="list-group-item list-group-item-action active" aria-current="true">
<i class="bi bi-person-fill-gear me-2"></i>Profile
</a>
<a href="#" class="list-group-item list-group-item-action"><i class="bi bi-journal-text me-2"></i>My Bookings</a>
<a href="#" class="list-group-item list-group-item-action"><i class="bi bi-car-front-fill me-2"></i>My Cars for Sale</a>
<a href="logout.php" class="list-group-item list-group-item-action text-danger"><i class="bi bi-box-arrow-left me-2"></i>Logout</a>
</div>
</div>
</div>
<!-- Main Content -->
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Welcome to your Dashboard</h4>
</div>
<div class="card-body">
<h5 class="card-title">Your Profile Information</h5>
<form>
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" value="<?php echo htmlspecialchars($user['username']); ?>" readonly>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" value="<?php echo htmlspecialchars($user['email']); ?>" readonly>
</div>
<div class="mb-3">
<label for="memberSince" class="form-label">Member Since</label>
<input type="text" class="form-control" id="memberSince" value="<?php echo date("F j, Y", strtotime($user['created_at'])); ?>" readonly>
</div>
<button type="submit" class="btn btn-primary">Edit Profile (Coming Soon)</button>
</form>
</div>
</div>
</div>
</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>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

View File

@ -1,17 +1,51 @@
<?php
// Generated by setup_mariadb_project.sh — edit as needed.
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'app_37650');
define('DB_USER', 'app_37650');
define('DB_PASS', '4edb6fac-49c4-44d0-9b3e-6c6f933a7200');
// db/config.php
// Database configuration with fallback for XAMPP/Localhost
// 1. Try environment variables (for Cloud/VM environments)
$db_host = getenv('DB_HOST');
$db_name = getenv('DB_NAME');
$db_user = getenv('DB_USER');
$db_pass = getenv('DB_PASS');
// 2. Fallback to XAMPP defaults if not set
if ($db_host === false) $db_host = 'localhost';
if ($db_name === false) $db_name = 'car_dealership';
if ($db_user === false) $db_user = 'root';
if ($db_pass === false) $db_pass = ''; // Default XAMPP password is empty
// Define constants if not already defined
if (!defined('DB_HOST')) define('DB_HOST', $db_host);
if (!defined('DB_NAME')) define('DB_NAME', $db_name);
if (!defined('DB_USER')) define('DB_USER', $db_user);
if (!defined('DB_PASS')) define('DB_PASS', $db_pass);
function db() {
static $pdo;
if (!$pdo) {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
return $pdo;
}
static $pdo;
if (!$pdo) {
try {
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4';
$pdo = new PDO($dsn, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
// Special handling: If database not found, try connecting without DB name
// This is useful for the installer script to create the DB
if ($e->getCode() == 1049) { // Unknown database
$dsn_no_db = 'mysql:host='.DB_HOST.';charset=utf8mb4';
try {
$pdo = new PDO($dsn_no_db, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $ex) {
die("Database connection failed: " . $ex->getMessage());
}
} else {
die("Database connection failed: " . $e->getMessage());
}
}
}
return $pdo;
}

28
db/migrate.php Normal file
View File

@ -0,0 +1,28 @@
<?php
require_once 'config.php';
try {
$pdo = db();
// Create migrations table if it doesn't exist
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (migration VARCHAR(255) PRIMARY KEY)");
// Get executed migrations
$executed_migrations = $pdo->query("SELECT migration FROM migrations")->fetchAll(PDO::FETCH_COLUMN);
$migration_files = glob(__DIR__ . '/migrations/*.sql');
foreach ($migration_files as $file) {
$migration_name = basename($file);
if (!in_array($migration_name, $executed_migrations)) {
$sql = file_get_contents($file);
$pdo->exec($sql);
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
$stmt->execute([$migration_name]);
echo "Applied migration: " . $migration_name . "\n";
}
}
} catch (PDOException $e) {
die("Migration failed: " . $e->getMessage());
}

View File

@ -0,0 +1 @@
CREATE TABLE IF NOT EXISTS migrations (migration VARCHAR(255) PRIMARY KEY);

View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN IF NOT EXISTS status VARCHAR(50) NOT NULL DEFAULT 'active';

View File

@ -0,0 +1 @@
ALTER TABLE cars ADD COLUMN IF NOT EXISTS status VARCHAR(50) NOT NULL DEFAULT 'for_sale', ADD COLUMN IF NOT EXISTS description TEXT;

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS 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', -- pending, approved, canceled
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (car_id) REFERENCES cars(id) ON DELETE CASCADE
);

View File

@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS 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,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(50) NOT NULL DEFAULT 'pending', -- pending, approved
FOREIGN KEY (car_id) REFERENCES cars(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

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;

98
db/setup_cars.php Normal file
View File

@ -0,0 +1,98 @@
<?php
require_once __DIR__ . '/config.php';
try {
$pdo = db();
echo "Connected to database successfully.<br>";
// Idempotently add columns to the cars table if they don't exist
$columns_to_add = [
'status' => "ALTER TABLE cars ADD COLUMN status VARCHAR(50) NOT NULL DEFAULT 'pending'",
'color' => "ALTER TABLE cars ADD COLUMN color VARCHAR(50)",
'province' => "ALTER TABLE cars ADD COLUMN province VARCHAR(100)",
'city' => "ALTER TABLE cars ADD COLUMN city VARCHAR(100)",
'description' => "ALTER TABLE cars ADD COLUMN description TEXT",
];
$stmt = $pdo->query("DESCRIBE cars");
$existing_columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
foreach ($columns_to_add as $column => $sql) {
if (!in_array($column, $existing_columns)) {
$pdo->exec($sql);
echo "Column '{$column}' added to 'cars' table.<br>";
}
}
// Truncate to ensure exactly 10 cars as requested
$pdo->exec("SET FOREIGN_KEY_CHECKS = 0;");
$pdo->exec("TRUNCATE TABLE cars;");
$pdo->exec("SET FOREIGN_KEY_CHECKS = 1;");
echo "Table 'cars' truncated to fresh state.<br>";
$cars = [
[
'make' => 'Toyota', 'model' => 'Corolla', 'year' => 2018, 'price' => 13500, 'mileage' => 85000, 'color' => 'White', 'province' => 'Kabul', 'city' => 'Kabul', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/1545743/pexels-photo-1545743.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'A well-maintained Corolla, very popular in Kabul. Clean interior, smooth engine. -5 plate number.'
],
[
'make' => 'Toyota', 'model' => 'Land Cruiser', 'year' => 2020, 'price' => 75000, 'mileage' => 45000, 'color' => 'Black', 'province' => 'Herat', 'city' => 'Herat', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/3764984/pexels-photo-3764984.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'Powerful V8 Land Cruiser (VXR). Full option, sunroof, leather seats. Imported from Dubai.'
],
[
'make' => 'Mercedes-Benz', 'model' => 'C200', 'year' => 2016, 'price' => 22000, 'mileage' => 72000, 'color' => 'Silver', 'province' => 'Balkh', 'city' => 'Mazar-i-Sharif', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/241316/pexels-photo-241316.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'German luxury and comfort. Smooth ride with a clean interior. Perfect for Mazar city roads.'
],
[
'make' => 'Toyota', 'model' => 'Hilux', 'year' => 2021, 'price' => 35000, 'mileage' => 25000, 'color' => 'Red', 'province' => 'Kandahar', 'city' => 'Kandahar', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/248747/pexels-photo-248747.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'Robust and versatile Hilux (Surf style). Excellent for both city and rural roads in Kandahar.'
],
[
'make' => 'Honda', 'model' => 'Civic', 'year' => 2019, 'price' => 17000, 'mileage' => 55000, 'color' => 'Blue', 'province' => 'Kabul', 'city' => 'Kabul', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/1637859/pexels-photo-1637859.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'Sporty and modern Honda Civic. Features a sunroof and great fuel economy. American import.'
],
[
'make' => 'Ford', 'model' => 'Ranger', 'year' => 2017, 'price' => 24000, 'mileage' => 95000, 'color' => 'Gray', 'province' => 'Nangarhar', 'city' => 'Jalalabad', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/119435/pexels-photo-119435.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'American toughness. This Ford Ranger is built to handle tough terrain in Jalalabad area.'
],
[
'make' => 'Toyota', 'model' => 'RAV4', 'year' => 2018, 'price' => 21000, 'mileage' => 62000, 'color' => 'White', 'province' => 'Ghazni', 'city' => 'Ghazni', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/707046/pexels-photo-707046.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'Family-friendly SUV. Spacious and comfortable for long journeys between provinces.'
],
[
'make' => 'Lexus', 'model' => 'LX 570', 'year' => 2019, 'price' => 85000, 'mileage' => 40000, 'color' => 'Pearl White', 'province' => 'Kabul', 'city' => 'Kabul', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/116675/pexels-photo-116675.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'The pinnacle of luxury and capability. Top-of-the-line model with all options. Bulletproof glass.'
],
[
'make' => 'Toyota', 'model' => 'Camry', 'year' => 2017, 'price' => 18500, 'mileage' => 75000, 'color' => 'Silver', 'province' => 'Kunduz', 'city' => 'Kunduz', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/1035108/pexels-photo-1035108.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'Reliable and fuel-efficient Camry. Smooth driving and very comfortable seats.'
],
[
'make' => 'Suzuki', 'model' => 'Alto', 'year' => 2015, 'price' => 4500, 'mileage' => 110000, 'color' => 'Red', 'province' => 'Bamyan', 'city' => 'Bamyan', 'status' => 'approved',
'image_url' => 'https://images.pexels.com/photos/1035108/pexels-photo-1035108.jpeg?auto=compress&cs=tinysrgb&w=800',
'description' => 'Small and economical Alto. Great for narrow streets and very low fuel consumption.'
],
];
$insertSql = "INSERT INTO cars (make, model, year, price, mileage, color, province, city, status, image_url, description) VALUES (:make, :model, :year, :price, :mileage, :color, :province, :city, :status, :image_url, :description)";
$stmt = $pdo->prepare($insertSql);
$count = 0;
foreach ($cars as $car) {
$stmt->execute($car);
$count++;
}
echo "Seeded " . $count . " cars into the database.<br>";
} catch (PDOException $e) {
echo "Database error: " . $e->getMessage();
}

60
db/setup_users.php Normal file
View File

@ -0,0 +1,60 @@
<?php
require_once 'config.php';
try {
$pdo = db();
$sql = "
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
role ENUM('user', 'admin') NOT NULL DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
";
$pdo->exec($sql);
echo "Table 'users' created successfully." . PHP_EOL;
// Add or update the admin user
$username = 'admin';
$email = 'admin@gmail.com';
$password = '12345678';
$password_hash = password_hash($password, PASSWORD_DEFAULT);
$role = 'admin';
// Check if admin already exists by email
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = :email LIMIT 1");
$stmt->execute([':email' => $email]);
$admin = $stmt->fetch();
if ($admin) {
// Update the existing admin
$update_sql = "UPDATE users SET username = :username, password_hash = :password_hash, role = :role WHERE id = :id";
$update_stmt = $pdo->prepare($update_sql);
$update_stmt->execute([
':username' => $username,
':password_hash' => $password_hash,
':role' => $role,
':id' => $admin['id']
]);
echo "Admin user updated. Username: '$username', Email: '$email'." . PHP_EOL;
} else {
// User does not exist, insert new admin user
$insert_sql = "
INSERT INTO users (username, email, password_hash, role)
VALUES (:username, :email, :password_hash, :role);
";
$insert_stmt = $pdo->prepare($insert_sql);
$insert_stmt->execute([
':username' => $username,
':email' => $email,
':password_hash' => $password_hash,
':role' => $role
]);
echo "Default admin user created. Username: '$username', Email: '$email' (Password: $password)." . PHP_EOL;
}
} catch (PDOException $e) {
die("DB ERROR: " . $e->getMessage());
}

51
includes/functions.php Normal file
View File

@ -0,0 +1,51 @@
<?php
require_once __DIR__ . '/../db/config.php';
/**
* Safe user login and authentication
*
* @param string $username
* @param string $password
* @return array|false Returns user array if successful, false otherwise
*/
function login_user($username, $password) {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username LIMIT 1");
$stmt->execute([':username' => $username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
return $user;
}
return false;
}
/**
* Fetch car listings
*
* @param array $filters Optional filters (make, province, etc.)
* @return array List of cars
*/
function fetch_cars($filters = []) {
$pdo = db();
$sql = "SELECT * FROM cars";
$params = [];
$where = [];
if (!empty($filters['status'])) {
$where[] = "status = :status";
$params[':status'] = $filters['status'];
}
// Add more filters as needed based on the app's requirements
if (!empty($where)) {
$sql .= " WHERE " . implode(" AND ", $where);
}
$sql .= " ORDER BY created_at DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}

356
index.php
View File

@ -1,150 +1,216 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
require_once __DIR__ . '/db/config.php';
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
$pdo = db();
// Fetch Data for Homepage
try {
// Stats
$stats = [
'total_cars' => $pdo->query("SELECT COUNT(*) FROM cars")->fetchColumn(),
'approved_cars' => $pdo->query("SELECT COUNT(*) FROM cars WHERE status = 'approved'")->fetchColumn(),
'cars_sold' => $pdo->query("SELECT COUNT(*) FROM bookings WHERE status = 'approved'")->fetchColumn(),
];
// Recently Added (Limit 4)
$stmt = $pdo->query("SELECT * FROM cars WHERE status = 'approved' ORDER BY created_at DESC LIMIT 4");
$recentCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Featured (Random 5 for Carousel)
$stmt = $pdo->query("SELECT * FROM cars WHERE status = 'approved' ORDER BY RAND() LIMIT 5");
$featuredCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
error_log("DB Error: " . $e->getMessage());
}
$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" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
<!-- Hero Carousel -->
<div id="heroCarousel" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-indicators">
<?php foreach ($featuredCars as $index => $car): ?>
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="<?= $index ?>" class="<?= $index === 0 ? 'active' : '' ?>" aria-current="<?= $index === 0 ? 'true' : 'false' ?>" aria-label="Slide <?= $index + 1 ?>"></button>
<?php endforeach; ?>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<div class="carousel-inner">
<?php foreach ($featuredCars as $index => $car): ?>
<div class="carousel-item <?= $index === 0 ? 'active' : '' ?>" style="height: 600px;">
<div class="overlay" style="background: rgba(0,0,0,0.4); position: absolute; top:0; left:0; width:100%; height:100%;"></div>
<img src="<?= htmlspecialchars($car['image_url'] ?: 'assets/images/placeholder.jpg') ?>" class="d-block w-100 h-100 object-fit-cover" alt="<?= htmlspecialchars($car['title']) ?>">
<div class="carousel-caption d-none d-md-block text-start mb-5 pb-5">
<div class="container">
<span class="badge bg-primary mb-2 text-uppercase ls-1">Featured</span>
<h1 class="display-3 fw-bold"><?= htmlspecialchars($car['year'] . ' ' . $car['make'] . ' ' . $car['model']) ?></h1>
<p class="lead mb-4"><?= htmlspecialchars(substr($car['description'], 0, 100)) ?>...</p>
<a href="car_detail.php?id=<?= $car['id'] ?>" class="btn btn-primary btn-lg px-4">View Details <i class="bi bi-arrow-right ms-2"></i></a>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#heroCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#heroCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
<!-- Quick Search Box -->
<div class="container position-relative" style="margin-top: -80px; z-index: 10;">
<div class="card shadow border-0 p-4">
<form action="car_list.php" method="GET" class="row g-3 align-items-end">
<div class="col-md-3">
<label class="form-label fw-bold small text-uppercase">Make</label>
<select name="make" class="form-select border-0 bg-light">
<option value="">All Makes</option>
<option value="Toyota">Toyota</option>
<option value="Honda">Honda</option>
<option value="Ford">Ford</option>
<option value="Mercedes-Benz">Mercedes-Benz</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold small text-uppercase">Province</label>
<select name="province" class="form-select border-0 bg-light">
<option value="">All Provinces</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>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold small text-uppercase">Max Price</label>
<input type="number" name="max_price" class="form-control border-0 bg-light" placeholder="e.g. 20000">
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-primary w-100 py-2"><i class="bi bi-search me-2"></i> Search Cars</button>
</div>
</form>
</div>
</div>
<!-- Stats Section -->
<section class="section-padding py-5 mt-5">
<div class="container">
<div class="row g-4 text-center">
<div class="col-md-4">
<div class="p-4 border rounded-3 bg-white shadow-sm h-100">
<i class="bi bi-car-front fs-1 text-primary mb-3"></i>
<h2 class="fw-bold display-5"><?= number_format($stats['approved_cars']) ?>+</h2>
<p class="text-muted mb-0">Cars Available for Sale</p>
</div>
</div>
<div class="col-md-4">
<div class="p-4 border rounded-3 bg-white shadow-sm h-100">
<i class="bi bi-people fs-1 text-success mb-3"></i>
<h2 class="fw-bold display-5"><?= number_format($stats['cars_sold']) ?>+</h2>
<p class="text-muted mb-0">Happy Customers</p>
</div>
</div>
<div class="col-md-4">
<div class="p-4 border rounded-3 bg-white shadow-sm h-100">
<i class="bi bi-check-circle fs-1 text-info mb-3"></i>
<h2 class="fw-bold display-5">100%</h2>
<p class="text-muted mb-0">Verified Listings</p>
</div>
</div>
</div>
</div>
</section>
<!-- Recently Added -->
<section class="section-padding py-5 bg-light">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-5">
<div>
<h2 class="fw-bold">Recently Added</h2>
<p class="text-muted mb-0">Fresh arrivals just for you</p>
</div>
<a href="car_list.php" class="btn btn-outline-primary">View All <i class="bi bi-arrow-right ms-1"></i></a>
</div>
<div class="row g-4">
<?php foreach ($recentCars as $car): ?>
<div class="col-md-6 col-lg-3">
<div class="card h-100 border-0 shadow-sm car-card hover-lift">
<div class="position-relative">
<img src="<?= htmlspecialchars($car['image_url'] ?: 'assets/images/placeholder.jpg') ?>" class="card-img-top" alt="<?= htmlspecialchars($car['title']) ?>" style="height: 220px; object-fit: cover;">
<span class="badge bg-white text-dark position-absolute top-0 end-0 m-3 shadow-sm"><?= htmlspecialchars($car['year']) ?></span>
</div>
<div class="card-body">
<h5 class="card-title text-truncate"><?= htmlspecialchars($car['make'] . ' ' . $car['model']) ?></h5>
<p class="card-text text-muted small"><i class="bi bi-geo-alt-fill text-danger"></i> <?= htmlspecialchars($car['city']) ?></p>
<div class="d-flex justify-content-between align-items-center mt-3">
<span class="h5 mb-0 text-primary fw-bold">$<?= number_format($car['price']) ?></span>
<a href="car_detail.php?id=<?= $car['id'] ?>" class="btn btn-sm btn-light rounded-circle"><i class="bi bi-arrow-right"></i></a>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<!-- Testimonials -->
<section class="section-padding py-5">
<div class="container">
<div class="text-center mb-5">
<h2 class="fw-bold">What Our Users Say</h2>
<p class="text-muted">Trusted by thousands of sellers and buyers across Afghanistan.</p>
</div>
<div class="row g-4">
<div class="col-md-4">
<div class="card border-0 shadow-sm p-4 h-100 text-center">
<div class="mb-3">
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
</div>
<p class="fst-italic text-muted">"Sold my Corolla in just 2 days! The process was super smooth and the admin support was helpful."</p>
<h6 class="fw-bold mt-auto">- Ahmad Wali, Kabul</h6>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm p-4 h-100 text-center">
<div class="mb-3">
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-half text-warning"></i>
</div>
<p class="fst-italic text-muted">"Found a great deal on a Ford Ranger. The car was exactly as described. Highly recommended!"</p>
<h6 class="fw-bold mt-auto">- Fatima Noor, Herat</h6>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm p-4 h-100 text-center">
<div class="mb-3">
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
</div>
<p class="fst-italic text-muted">"Best platform for cars in Afghanistan. Secure, fast, and easy to use."</p>
<h6 class="fw-bold mt-auto">- Mustafa Khan, Mazar</h6>
</div>
</div>
</div>
</div>
</section>
<?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>
</html>

632
install.php Normal file
View File

@ -0,0 +1,632 @@
<?php
// install.php
// Single installation file to set up the project on Windows (XAMPP) or Linux.
require_once 'db/config.php';
// Disable output buffering to show progress in real-time
if (function_exists('apache_setenv')) {
@apache_setenv('no-gzip', 1);
}
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);
echo "<!DOCTYPE html><html><head><title>Installation</title><style>body{font-family: sans-serif; padding: 20px; line-height: 1.6;} .success{color: green;} .error{color: red;} .step{margin-bottom: 10px;}</style></head><body>";
echo "<h1>Car Sells in Afghanistan - Installation</h1>";
try {
// 1. Connect to Database Server
$pdo = db();
echo "<div class='step success'>✅ Connected to Database Server.</div>";
// 2. Create Database (if it doesn't exist)
$dbName = DB_NAME;
try {
$pdo->exec("CREATE DATABASE IF NOT EXISTS `$dbName` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
echo "<div class='step success'>✅ Database `$dbName` checked/created.</div>";
} catch (PDOException $e) {
echo "<div class='step'> Note: Could not create database (might already exist or permission denied). Proceeding...</div>";
}
// Select the database
$pdo->exec("USE `$dbName`");
// 3. Drop existing tables (Clean Install)
$pdo->exec("SET FOREIGN_KEY_CHECKS=0");
$tables = ['reviews', 'bookings', 'cars', 'users'];
foreach ($tables as $table) {
$pdo->exec("DROP TABLE IF EXISTS `$table`");
}
$pdo->exec("SET FOREIGN_KEY_CHECKS=1");
echo "<div class='step success'>✅ Existing tables dropped (Clean Install).</div>";
// 4. Create Tables
// Users Table
$pdo->exec("CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role VARCHAR(20) DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
echo "<div class='step success'>✅ Table `users` created.</div>";
// Cars Table
$pdo->exec("CREATE TABLE cars (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NULL,
title VARCHAR(255) NULL,
make VARCHAR(100),
model VARCHAR(100),
year INT,
mileage INT,
price DECIMAL(10,2),
description TEXT,
status VARCHAR(50) NOT NULL DEFAULT 'pending',
color VARCHAR(50),
province VARCHAR(100),
city VARCHAR(100),
image_url VARCHAR(255) DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
)");
echo "<div class='step success'>✅ Table `cars` created.</div>";
// 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 "<div class='step success'>✅ Table `bookings` created.</div>";
// 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 "<div class='step success'>✅ Table `reviews` created.</div>";
// 5. Create Default Admin User
$adminUser = 'admin';
$adminEmail = 'admin@gmail.com';
$adminPass = '123'; // As requested
$adminHash = password_hash($adminPass, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (username, email, password, role) VALUES (?, ?, ?, 'admin')");
$stmt->execute([$adminUser, $adminEmail, $adminHash]);
$adminId = $pdo->lastInsertId();
echo "<div class='step success'>✅ Admin user created.<br> &nbsp;&nbsp; Username: <b>$adminUser</b><br> &nbsp;&nbsp; Email: <b>$adminEmail</b><br> &nbsp;&nbsp; Password: <b>$adminPass</b></div>";
// 6. Insert Sample Data (Cars)
// 35 Total Cars (15 original + 20 new)
$carsData = [
// Original 15
[
'title' => 'Toyota Corolla 2020 Clean',
'make' => 'Toyota',
'model' => 'Corolla',
'year' => 2020,
'mileage' => 15000,
'price' => 18500.00,
'description' => 'Very clean car, no accidents. Perfect for city driving.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/112460/pexels-photo-112460.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Honda Civic 2018 Sport',
'make' => 'Honda',
'model' => 'Civic',
'year' => 2018,
'mileage' => 45000,
'price' => 16200.00,
'description' => 'Sport mode, leather seats, sunroof. Excellent condition.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Herat',
'city' => 'Herat',
'image_url' => 'https://images.pexels.com/photos/170811/pexels-photo-170811.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Ford Ranger 2019 4x4',
'make' => 'Ford',
'model' => 'Ranger',
'year' => 2019,
'mileage' => 30000,
'price' => 25000.00,
'description' => 'Strong pickup for tough roads. 4x4 capability.',
'status' => 'pending',
'color' => 'Blue',
'province' => 'Kandahar',
'city' => 'Kandahar',
'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'
],
[
'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'
],
// New 20 Cars
[
'title' => 'Tesla Model 3 2022 Long Range',
'make' => 'Tesla',
'model' => 'Model 3',
'year' => 2022,
'mileage' => 8000,
'price' => 45000.00,
'description' => 'Electric future. Autopilot included. Mint condition.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/11139552/pexels-photo-11139552.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Ford Mustang 2018 GT',
'make' => 'Ford',
'model' => 'Mustang',
'year' => 2018,
'mileage' => 32000,
'price' => 38000.00,
'description' => 'V8 Muscle car. Sounds amazing.',
'status' => 'approved',
'color' => 'Yellow',
'province' => 'Herat',
'city' => 'Herat',
'image_url' => 'https://images.pexels.com/photos/3311574/pexels-photo-3311574.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Audi Q7 2019 Quattro',
'make' => 'Audi',
'model' => 'Q7',
'year' => 2019,
'mileage' => 41000,
'price' => 52000.00,
'description' => '7 seater luxury SUV. Smooth ride.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/1035108/pexels-photo-1035108.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Jeep Wrangler 2020 Rubicon',
'make' => 'Jeep',
'model' => 'Wrangler',
'year' => 2020,
'mileage' => 15000,
'price' => 48000.00,
'description' => 'Ultimate off-road machine. Convertible top.',
'status' => 'approved',
'color' => 'Red',
'province' => 'Jalalabad',
'city' => 'Jalalabad',
'image_url' => 'https://images.pexels.com/photos/2933243/pexels-photo-2933243.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Hyundai Tucson 2022',
'make' => 'Hyundai',
'model' => 'Tucson',
'year' => 2022,
'mileage' => 5000,
'price' => 31000.00,
'description' => 'Modern design, very comfortable.',
'status' => 'approved',
'color' => 'Grey',
'province' => 'Mazar-i-Sharif',
'city' => 'Mazar',
'image_url' => 'https://images.pexels.com/photos/13622998/pexels-photo-13622998.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Range Rover Sport 2017',
'make' => 'Land Rover',
'model' => 'Range Rover',
'year' => 2017,
'mileage' => 60000,
'price' => 65000.00,
'description' => 'British luxury. Powerful engine.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/116675/pexels-photo-116675.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Volkswagen Golf 2016 GTI',
'make' => 'Volkswagen',
'model' => 'Golf',
'year' => 2016,
'mileage' => 70000,
'price' => 18000.00,
'description' => 'Hot hatch. Fast and fun.',
'status' => 'approved',
'color' => 'Red',
'province' => 'Kandahar',
'city' => 'Kandahar',
'image_url' => 'https://images.pexels.com/photos/10771143/pexels-photo-10771143.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Subaru Forester 2018',
'make' => 'Subaru',
'model' => 'Forester',
'year' => 2018,
'mileage' => 55000,
'price' => 22000.00,
'description' => 'Reliable AWD. Great for snow.',
'status' => 'approved',
'color' => 'Green',
'province' => 'Bamyan',
'city' => 'Bamyan',
'image_url' => 'https://images.pexels.com/photos/15671190/pexels-photo-15671190.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Porsche Cayenne 2015',
'make' => 'Porsche',
'model' => 'Cayenne',
'year' => 2015,
'mileage' => 85000,
'price' => 35000.00,
'description' => 'Sporty SUV. High performance.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/9661271/pexels-photo-9661271.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Toyota RAV4 2021',
'make' => 'Toyota',
'model' => 'RAV4',
'year' => 2021,
'mileage' => 12000,
'price' => 29000.00,
'description' => 'Best selling SUV. Reliable.',
'status' => 'approved',
'color' => 'Blue',
'province' => 'Herat',
'city' => 'Herat',
'image_url' => 'https://images.pexels.com/photos/14532256/pexels-photo-14532256.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Nissan Patrol 2019',
'make' => 'Nissan',
'model' => 'Patrol',
'year' => 2019,
'mileage' => 45000,
'price' => 58000.00,
'description' => 'King of the desert. Spacious.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kandahar',
'city' => 'Kandahar',
'image_url' => 'https://images.pexels.com/photos/1592384/pexels-photo-1592384.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Chevrolet Camaro 2017',
'make' => 'Chevrolet',
'model' => 'Camaro',
'year' => 2017,
'mileage' => 38000,
'price' => 27000.00,
'description' => 'American icon. Fast.',
'status' => 'approved',
'color' => 'Yellow',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/2036544/pexels-photo-2036544.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Mercedes-Benz G-Wagon 2018',
'make' => 'Mercedes-Benz',
'model' => 'G-Class',
'year' => 2018,
'mileage' => 30000,
'price' => 120000.00,
'description' => 'Status symbol. Built like a tank.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/1429775/pexels-photo-1429775.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Kia Sorento 2021',
'make' => 'Kia',
'model' => 'Sorento',
'year' => 2021,
'mileage' => 10000,
'price' => 34000.00,
'description' => 'Family SUV with 3 rows.',
'status' => 'approved',
'color' => 'Grey',
'province' => 'Mazar-i-Sharif',
'city' => 'Mazar',
'image_url' => 'https://images.pexels.com/photos/9714349/pexels-photo-9714349.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Lexus RX350 2016',
'make' => 'Lexus',
'model' => 'RX350',
'year' => 2016,
'mileage' => 65000,
'price' => 32000.00,
'description' => 'Comfortable luxury crossover.',
'status' => 'approved',
'color' => 'White',
'province' => 'Herat',
'city' => 'Herat',
'image_url' => 'https://images.pexels.com/photos/6560298/pexels-photo-6560298.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Hyundai Elantra 2019',
'make' => 'Hyundai',
'model' => 'Elantra',
'year' => 2019,
'mileage' => 40000,
'price' => 14000.00,
'description' => 'Compact sedan, good value.',
'status' => 'approved',
'color' => 'Blue',
'province' => 'Jalalabad',
'city' => 'Jalalabad',
'image_url' => 'https://images.pexels.com/photos/16834162/pexels-photo-16834162.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Toyota Prius 2017',
'make' => 'Toyota',
'model' => 'Prius',
'year' => 2017,
'mileage' => 75000,
'price' => 15500.00,
'description' => 'Hybrid pioneer. Excellent mpg.',
'status' => 'approved',
'color' => 'White',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/17604374/pexels-photo-17604374.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Ford F-150 2020',
'make' => 'Ford',
'model' => 'F-150',
'year' => 2020,
'mileage' => 25000,
'price' => 40000.00,
'description' => 'America\'s best selling truck.',
'status' => 'approved',
'color' => 'Blue',
'province' => 'Kandahar',
'city' => 'Kandahar',
'image_url' => 'https://images.pexels.com/photos/1637859/pexels-photo-1637859.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'BMW 3 Series 2018',
'make' => 'BMW',
'model' => '320i',
'year' => 2018,
'mileage' => 42000,
'price' => 25000.00,
'description' => 'Sports sedan. Driving pleasure.',
'status' => 'approved',
'color' => 'Silver',
'province' => 'Kabul',
'city' => 'Kabul',
'image_url' => 'https://images.pexels.com/photos/3689532/pexels-photo-3689532.jpeg?auto=compress&cs=tinysrgb&w=600'
],
[
'title' => 'Audi A6 2017',
'make' => 'Audi',
'model' => 'A6',
'year' => 2017,
'mileage' => 58000,
'price' => 26000.00,
'description' => 'Executive sedan. High tech.',
'status' => 'approved',
'color' => 'Black',
'province' => 'Herat',
'city' => 'Herat',
'image_url' => 'https://images.pexels.com/photos/119435/pexels-photo-119435.jpeg?auto=compress&cs=tinysrgb&w=600'
]
];
$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 "<div class='step success'>✅ Seed data inserted (" . count($carsData) . " cars).</div>";
echo "<hr><h2>🎉 Installation Complete!</h2>";
echo "<p>You can now <a href='login.php'>Login here</a>.</p>";
echo "<p><b>Credentials:</b><br>Username: <code>admin</code> or <code>admin@gmail.com</code><br>Password: <code>123</code></p>";
} catch (PDOException $e) {
echo "<div class='step error'>❌ Installation Failed: " . htmlspecialchars($e->getMessage()) . "</div>";
}
echo "</body></html>";
?>

116
login.php Normal file
View File

@ -0,0 +1,116 @@
<?php
require_once 'db/config.php';
session_start();
if (isset($_SESSION['user_id'])) {
// Redirect to the appropriate dashboard if already logged in
if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') {
header("Location: admin/index.php");
} else {
header("Location: dashboard.php");
}
exit();
}
$errors = [];
$login_input = ''; // Store input to repopulate form
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$login_input = trim($_POST['username'] ?? ''); // This field now accepts user OR email
$password = $_POST['password'] ?? '';
if (empty($login_input)) {
$errors[] = 'Username or Email is required.';
}
if (empty($password)) {
$errors[] = 'Password is required.';
}
if (empty($errors)) {
try {
$pdo = db();
// Allow login by username OR email
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :input OR email = :input LIMIT 1");
$stmt->execute(['input' => $login_input]);
$user = $stmt->fetch();
// Note: The 'password' column stores the hash
if ($user && password_verify($password, $user['password'])) {
// Login Success
$_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.';
}
} catch (PDOException $e) {
error_log("Database error: " . $e->getMessage());
$errors[] = "An internal error occurred. Please try again later.";
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Car Sells in 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=<?php echo time(); ?>">
</head>
<body>
<div class="form-container">
<div class="card auth-card shadow-lg border-0">
<div class="card-body p-5">
<div class="text-center mb-5">
<h1 class="h2 fw-bold">Welcome Back!</h1>
<p class="text-muted">Login to access your account.</p>
</div>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger" role="alert">
<?php foreach ($errors as $error): ?>
<p class="mb-0 small"><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form action="login.php" method="POST">
<div class="mb-4">
<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 or admin@gmail.com" required value="<?php echo htmlspecialchars($login_input); ?>">
</div>
<div class="mb-4">
<label for="password" class="form-label fw-semibold">Password</label>
<input type="password" id="password" name="password" class="form-control form-control-lg" placeholder="••••••••" required>
</div>
<div class="d-grid mt-5">
<button type="submit" class="btn btn-primary btn-lg shadow-sm">Login</button>
</div>
</form>
<p class="text-center text-muted mt-4">
Don't have an account? <a href="register.php" class="text-primary text-decoration-none fw-semibold">Create one now</a>.
</p>
<p class="text-center text-muted mt-2">
<a href="index.php" class="text-muted text-decoration-none small"><i class="bi bi-arrow-left"></i> Back to Home</a>
</p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

6
logout.php Normal file
View File

@ -0,0 +1,6 @@
<?php
session_start();
session_unset();
session_destroy();
header("Location: index.php");
exit();

46
partials/footer.php Normal file
View File

@ -0,0 +1,46 @@
<footer class="footer py-5">
<div class="container">
<div class="row g-4">
<div class="col-lg-4 mb-4 mb-lg-0">
<h4 class="h5 fw-bold mb-3">Car Sells in Afghanistan</h4>
<p class="text-white-50">Your trusted partner in buying and selling quality cars in Afghanistan. We are committed to transparency, trust, and customer satisfaction in the Afghan automotive market.</p>
</div>
<div class="col-lg-2 col-md-4 col-6">
<h5 class="h6 fw-bold mb-3">Quick Links</h5>
<ul class="list-unstyled">
<li class="mb-2"><a href="index.php" class="text-decoration-none text-white-50">Home</a></li>
<li class="mb-2"><a href="car_list.php" class="text-decoration-none text-white-50">All Cars</a></li>
<li class="mb-2"><a href="about.php" class="text-decoration-none text-white-50">About Us</a></li>
<li class="mb-2"><a href="contact.php" class="text-decoration-none text-white-50">Contact</a></li>
</ul>
</div>
<div class="col-lg-3 col-md-4 col-6">
<h5 class="h6 fw-bold mb-3">Contact Us</h5>
<ul class="list-unstyled text-white-50">
<li class="mb-2"><i class="bi bi-geo-alt-fill me-2"></i>Kabul, Afghanistan</li>
<li class="mb-2"><i class="bi bi-telephone-fill me-2"></i>+93 700 123 456</li>
<li class="mb-2"><i class="bi bi-envelope-fill me-2"></i>info@carsells.af</li>
</ul>
</div>
<div class="col-lg-3 col-md-4">
<h5 class="h6 fw-bold mb-3">Follow Us</h5>
<p class="text-white-50 small">Stay updated with our latest additions and special offers across Afghanistan.</p>
<div class="d-flex gap-3">
<a href="#" class="text-white fs-5"><i class="bi bi-facebook"></i></a>
<a href="#" class="text-white fs-5"><i class="bi bi-twitter-x"></i></a>
<a href="#" class="text-white fs-5"><i class="bi bi-instagram"></i></a>
</div>
</div>
</div>
<hr class="my-5 border-secondary">
<div class="row align-items-center">
<div class="col-md-6 text-center text-md-start text-white-50 small">
<p class="mb-0">&copy; <?php echo date('Y'); ?> Car Sells in Afghanistan. All Rights Reserved.</p>
</div>
<div class="col-md-6 text-center text-md-end text-white-50 small mt-3 mt-md-0">
<a href="#" class="text-white-50 text-decoration-none me-3">Privacy Policy</a>
<a href="#" class="text-white-50 text-decoration-none">Terms of Service</a>
</div>
</div>
</div>
</footer>

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>

55
partials/navbar.php Normal file
View File

@ -0,0 +1,55 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$current_page = basename($_SERVER['PHP_SELF']);
?>
<nav class="navbar navbar-expand-lg sticky-top">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="index.php">
<span class="fw-bold">Car Sells in Afghanistan</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto align-items-center">
<li class="nav-item">
<a class="nav-link <?= ($current_page == 'index.php') ? 'active' : '' ?>" href="index.php">Home</a>
</li>
<li class="nav-item">
<a class="nav-link <?= ($current_page == 'car_list.php') ? 'active' : '' ?>" href="car_list.php">Cars</a>
</li>
<li class="nav-item">
<a class="nav-link <?= ($current_page == 'about.php') ? 'active' : '' ?>" href="about.php">About</a>
</li>
<li class="nav-item">
<a class="nav-link <?= ($current_page == 'contact.php') ? 'active' : '' ?>" href="contact.php">Contact</a>
</li>
<?php if (isset($_SESSION['user_id'])): ?>
<li class="nav-item dropdown ms-lg-3">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<img src="https://i.pravatar.cc/30?u=<?= htmlspecialchars($_SESSION['username']) ?>" alt="" class="rounded-circle me-2"><?= htmlspecialchars($_SESSION['username']); ?>
</a>
<ul class="dropdown-menu dropdown-menu-end shadow border-0" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="dashboard.php"><i class="bi bi-speedometer2 me-2"></i>Dashboard</a></li>
<?php if ($_SESSION['role'] === 'admin'): ?>
<li><a class="dropdown-item" href="admin/index.php"><i class="bi bi-shield-lock me-2"></i>Admin Panel</a></li>
<?php endif; ?>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="logout.php"><i class="bi bi-box-arrow-right me-2"></i>Logout</a></li>
</ul>
</li>
<?php else: ?>
<li class="nav-item ms-lg-3">
<a href="login.php" class="btn btn-outline-primary rounded-pill px-4">Login</a>
</li>
<li class="nav-item ms-2">
<a href="register.php" class="btn btn-primary rounded-pill px-4">Register</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>

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>

127
register.php Normal file
View File

@ -0,0 +1,127 @@
<?php
require_once 'db/config.php';
session_start();
if (isset($_SESSION['user_id'])) {
header("Location: dashboard.php");
exit();
}
$errors = [];
$username = '';
$email = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
// Validation
if (empty($username)) {
$errors[] = 'Username is required.';
}
if (empty($email)) {
$errors[] = 'Email is required.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Invalid email format.';
}
if (empty($password)) {
$errors[] = 'Password is required.';
} elseif (strlen($password) < 8) {
$errors[] = 'Password must be at least 8 characters long.';
}
if (empty($errors)) {
try {
$pdo = db();
// Check if username OR email already exists
$stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE username = :username OR email = :email");
$stmt->execute(['username' => $username, 'email' => $email]);
if ($stmt->fetchColumn() > 0) {
// Determine which one exists for better error message (optional but nice)
// For simplicity:
$errors[] = 'Username or Email is already taken.';
} else {
// Hash password and insert new user
$password_hash = password_hash($password, PASSWORD_DEFAULT);
// Schema: id, username, email, password, role, created_at
$insert_stmt = $pdo->prepare("INSERT INTO users (username, email, password, role) VALUES (:username, :email, :password, 'user')");
$insert_stmt->execute([
':username' => $username,
':email' => $email,
':password' => $password_hash
]);
// Log the user in immediately
$_SESSION['user_id'] = $pdo->lastInsertId();
$_SESSION['username'] = $username;
$_SESSION['role'] = 'user';
header("Location: dashboard.php");
exit();
}
} catch (PDOException $e) {
error_log("Database error: " . $e->getMessage());
$errors[] = 'An internal error occurred. Please try again later.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create Account - Car Sells in Afghanistan</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<div class="form-container">
<div class="card auth-card">
<div class="card-body">
<div class="text-center mb-5">
<h1 class="h2">Create Your Account</h1>
<p class="text-muted">Join us to find your dream car in Afghanistan.</p>
</div>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger" role="alert">
<?php foreach ($errors as $error): ?>
<p class="mb-0"><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form action="register.php" method="POST">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="e.g., ahmadwali" required value="<?php echo htmlspecialchars($username); ?>">
</div>
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input type="email" id="email" name="email" class="form-control" placeholder="e.g., ahmad@example.com" required value="<?php echo htmlspecialchars($email); ?>">
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Minimum 8 characters" required>
</div>
<div class="d-grid mt-4">
<button type="submit" class="btn btn-primary">Create Account</button>
</div>
</form>
<p class="text-center text-muted mt-4">
Already have an account? <a href="login.php">Login here</a>.
</p>
<p class="text-center text-muted mt-2">
<a href="index.php">Back to Home</a>
</p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</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>