Sindbad~EG File Manager
<?php
require_once '../includes/functions.php';
// Check if user is logged in and has admin privileges
if (!isLoggedIn()) {
header('Location: ' . BASE_URL . 'login.php');
exit();
}
$user = getCurrentUser();
if (!in_array($user['role'], ['superuser', 'area_admin', 'district_admin', 'assembly_admin'])) {
header('Location: ' . BASE_URL . 'dashboard.php');
exit();
}
$db = new CopMadinaDB();
$conn = $db->getConnection();
// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'create') {
$event_id = $_POST['event_id'];
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$price = $_POST['price'];
$category = trim($_POST['category']);
$stock_quantity = $_POST['stock_quantity'] ?: 0;
$status = $_POST['status'];
$stmt = executeQuery(
"INSERT INTO products (event_id, name, description, price, category, stock_quantity, status) VALUES (?, ?, ?, ?, ?, ?, ?)",
[$event_id, $name, $description, $price, $category, $stock_quantity, $status]
);
if ($stmt) {
logAudit('create', 'products', $conn->lastInsertId());
addNotification('success', 'Product created successfully!', $user['id']);
} else {
addNotification('error', 'Failed to create product.', $user['id']);
}
} elseif ($action === 'update') {
$id = $_POST['id'];
$event_id = $_POST['event_id'];
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$price = $_POST['price'];
$category = trim($_POST['category']);
$stock_quantity = $_POST['stock_quantity'] ?: 0;
$status = $_POST['status'];
$stmt = executeQuery(
"UPDATE products SET event_id = ?, name = ?, description = ?, price = ?, category = ?, stock_quantity = ?, status = ? WHERE id = ?",
[$event_id, $name, $description, $price, $category, $stock_quantity, $status, $id]
);
if ($stmt) {
logAudit('update', 'products', $id);
addNotification('success', 'Product updated successfully!', $user['id']);
} else {
addNotification('error', 'Failed to update product.', $user['id']);
}
} elseif ($action === 'delete') {
$id = $_POST['id'];
$stmt = executeQuery("DELETE FROM products WHERE id = ?", [$id]);
if ($stmt) {
logAudit('delete', 'products', $id);
addNotification('success', 'Product deleted successfully!', $user['id']);
} else {
addNotification('error', 'Failed to delete product.', $user['id']);
}
}
header('Location: merchandise.php');
exit();
}
// Get products with event information
$products_query = "
SELECT p.*, e.title as event_title
FROM products p
LEFT JOIN events e ON p.event_id = e.id
ORDER BY p.created_at DESC
";
$stmt = executeQuery($products_query);
$products = $stmt ? $stmt->fetchAll() : [];
// Get events for dropdown
$events_query = "SELECT id, title FROM events WHERE status = 'published' ORDER BY title";
$stmt = executeQuery($events_query);
$events = $stmt ? $stmt->fetchAll() : [];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Merchandise Management - COP Madina Conference</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.6s ease-out',
'scale-in': 'scaleIn 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0', transform: 'translateY(10px)' },
'100%': { opacity: '1', transform: 'translateY(0)' }
},
slideUp: {
'0%': { opacity: '0', transform: 'translateY(20px)' },
'100%': { opacity: '1', transform: 'translateY(0)' }
},
scaleIn: {
'0%': { opacity: '0', transform: 'scale(0.95)' },
'100%': { opacity: '1', transform: 'scale(1)' }
}
}
}
}
}
</script>
</head>
<body class="bg-gradient-to-br from-slate-50 to-blue-50 min-h-screen">
<div id="app" class="flex h-screen">
<!-- Sidebar -->
<?php include 'includes/admin_sidebar.php'; ?>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden ml-72">
<!-- Header -->
<header class="bg-white/80 backdrop-blur-sm shadow-lg border-b border-slate-200/50">
<div class="flex items-center justify-between px-8 py-6">
<div class="animate-fade-in">
<h1 class="text-3xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent flex items-center">
<div class="p-2 rounded-xl bg-gradient-to-br from-blue-500 to-purple-600 mr-3">
<i class="fas fa-shopping-bag text-white"></i>
</div>
Merchandise Management
</h1>
<p class="text-slate-600 mt-1">Manage event products and merchandise</p>
</div>
<button @click="showCreateModal = true"
class="px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white font-medium rounded-xl transition-all duration-200 flex items-center space-x-2 shadow-lg hover:shadow-xl animate-scale-in">
<i class="fas fa-plus"></i>
<span>Add New Product</span>
</button>
</div>
</header>
<!-- Content -->
<main class="flex-1 overflow-y-auto p-8">
<!-- Products Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<?php foreach ($products as $index => $product): ?>
<div class="group bg-white/70 backdrop-blur-sm rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 p-6 border border-slate-200/50 hover:border-blue-300/50 animate-slide-up"
style="animation-delay: <?php echo $index * 0.1; ?>s">
<!-- Product Header -->
<div class="flex items-start justify-between mb-4">
<div class="flex-1">
<h3 class="text-xl font-bold text-slate-800 mb-1"><?php echo htmlspecialchars($product['name']); ?></h3>
<p class="text-sm text-slate-600"><?php echo htmlspecialchars($product['event_title']); ?></p>
</div>
<span class="px-3 py-1 text-xs font-semibold rounded-full <?php
echo $product['status'] === 'active' ? 'bg-emerald-100 text-emerald-800' :
($product['status'] === 'out_of_stock' ? 'bg-red-100 text-red-800' : 'bg-gray-100 text-gray-800');
?>">
<?php echo ucfirst(str_replace('_', ' ', $product['status'])); ?>
</span>
</div>
<!-- Description -->
<?php if (!empty($product['description'])): ?>
<p class="text-sm text-slate-600 mb-4 line-clamp-2"><?php echo htmlspecialchars($product['description']); ?></p>
<?php endif; ?>
<!-- Product Details -->
<div class="grid grid-cols-2 gap-3 mb-4">
<div class="text-center p-3 bg-blue-50 rounded-xl">
<div class="text-lg font-bold text-blue-600">GH₵<?php echo number_format($product['price'], 2); ?></div>
<div class="text-xs text-slate-600">Price</div>
</div>
<div class="text-center p-3 bg-emerald-50 rounded-xl">
<div class="text-lg font-bold text-emerald-600"><?php echo $product['stock_quantity']; ?></div>
<div class="text-xs text-slate-600">Stock</div>
</div>
</div>
<!-- Category -->
<?php if (!empty($product['category'])): ?>
<div class="mb-4">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800">
<i class="fas fa-tag mr-1"></i>
<?php echo htmlspecialchars($product['category']); ?>
</span>
</div>
<?php endif; ?>
<!-- Actions -->
<div class="flex space-x-2">
<button @click="editProduct(<?php echo htmlspecialchars(json_encode($product)); ?>)"
class="flex-1 px-3 py-2 bg-blue-100 hover:bg-blue-200 text-blue-700 font-medium rounded-lg transition-colors text-sm">
<i class="fas fa-edit mr-1"></i>
Edit
</button>
<button @click="confirmDelete(<?php echo $product['id']; ?>, '<?php echo htmlspecialchars($product['name']); ?>')"
class="flex-1 px-3 py-2 bg-red-100 hover:bg-red-200 text-red-700 font-medium rounded-lg transition-colors text-sm">
<i class="fas fa-trash mr-1"></i>
Delete
</button>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- Empty State -->
<?php if (empty($products)): ?>
<div class="text-center py-16">
<div class="mx-auto w-24 h-24 bg-slate-100 rounded-full flex items-center justify-center mb-6">
<i class="fas fa-shopping-bag text-3xl text-slate-400"></i>
</div>
<h3 class="text-xl font-semibold text-slate-700 mb-2">No Products Found</h3>
<p class="text-slate-500 mb-6">Get started by creating your first product.</p>
<button @click="showCreateModal = true"
class="px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white font-medium rounded-xl transition-all duration-200">
<i class="fas fa-plus mr-2"></i>
Create First Product
</button>
</div>
<?php endif; ?>
</main>
</div>
<!-- Create/Edit Modal -->
<div v-if="showCreateModal || showEditModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<div class="bg-white/95 backdrop-blur-sm rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] overflow-y-auto border border-slate-200/50 animate-scale-in">
<div class="sticky top-0 bg-white/95 backdrop-blur-sm border-b border-slate-200/50 px-6 py-4 rounded-t-2xl">
<div class="flex items-center justify-between">
<h3 class="text-xl font-bold text-slate-800">
{{ showCreateModal ? 'Create New Product' : 'Edit Product' }}
</h3>
<button @click="closeModal()" class="p-2 rounded-lg hover:bg-slate-100 transition-colors">
<i class="fas fa-times text-slate-500"></i>
</button>
</div>
</div>
<form method="POST" class="p-6 space-y-6">
<input type="hidden" name="action" :value="showCreateModal ? 'create' : 'update'">
<input v-if="showEditModal" type="hidden" name="id" :value="editingProduct.id">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="md:col-span-2">
<label class="block text-sm font-semibold text-slate-700 mb-2">Event *</label>
<select name="event_id" :value="editingProduct.event_id" required
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200">
<option value="">Select an event</option>
<?php foreach ($events as $event): ?>
<option value="<?php echo $event['id']; ?>"><?php echo htmlspecialchars($event['title']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Product Name *</label>
<input type="text" name="name" :value="editingProduct.name" required
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
placeholder="Enter product name">
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Category</label>
<input type="text" name="category" :value="editingProduct.category"
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
placeholder="e.g., T-Shirts, Books, etc.">
</div>
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Description</label>
<textarea name="description" rows="3" :value="editingProduct.description"
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 resize-none"
placeholder="Enter product description"></textarea>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Price (GH₵) *</label>
<input type="number" name="price" step="0.01" min="0" :value="editingProduct.price" required
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
placeholder="0.00">
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Stock Quantity</label>
<input type="number" name="stock_quantity" min="0" :value="editingProduct.stock_quantity"
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
placeholder="0">
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Status</label>
<select name="status" :value="editingProduct.status"
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200">
<option value="active">Active</option>
<option value="inactive">Inactive</option>
<option value="out_of_stock">Out of Stock</option>
</select>
</div>
</div>
<div class="flex justify-end space-x-3 pt-4 border-t border-slate-200">
<button type="button" @click="closeModal()"
class="px-6 py-3 bg-slate-100 hover:bg-slate-200 text-slate-700 font-medium rounded-xl transition-colors">
Cancel
</button>
<button type="submit"
class="px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white font-medium rounded-xl transition-all duration-200 shadow-lg">
{{ showCreateModal ? 'Create Product' : 'Update Product' }}
</button>
</div>
</form>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div v-if="showDeleteModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<div class="bg-white/95 backdrop-blur-sm rounded-2xl shadow-2xl w-full max-w-md border border-slate-200/50 animate-scale-in">
<div class="p-6 text-center">
<div class="mx-auto w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mb-4">
<i class="fas fa-exclamation-triangle text-red-600 text-2xl"></i>
</div>
<h3 class="text-xl font-bold text-slate-800 mb-2">Delete Product</h3>
<p class="text-slate-600 mb-6">
Are you sure you want to delete <strong>"{{ deletingProductName }}"</strong>? This action cannot be undone.
</p>
<div class="flex justify-center space-x-3">
<form method="POST" class="flex space-x-3">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" :value="deletingProductId">
<button type="button" @click="showDeleteModal = false"
class="px-6 py-3 bg-slate-100 hover:bg-slate-200 text-slate-700 font-medium rounded-xl transition-colors">
Cancel
</button>
<button type="submit"
class="px-6 py-3 bg-red-600 hover:bg-red-700 text-white font-medium rounded-xl transition-colors">
Delete
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
showCreateModal: false,
showEditModal: false,
showDeleteModal: false,
editingProduct: {},
deletingProductId: null,
deletingProductName: ''
}
},
methods: {
editProduct(product) {
this.editingProduct = { ...product };
this.showEditModal = true;
},
confirmDelete(id, name) {
this.deletingProductId = id;
this.deletingProductName = name;
this.showDeleteModal = true;
},
closeModal() {
this.showCreateModal = false;
this.showEditModal = false;
this.editingProduct = {};
}
}
}).mount('#app');
</script>
</body>
</html>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists