222 lines
12 KiB
PHP
222 lines
12 KiB
PHP
<?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>
|