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') {
if (isset($_POST['action'])) {
switch ($_POST['action']) {
case 'add':
$stmt = $conn->prepare("INSERT INTO promo_codes (event_id, code, type, discount_value, usage_limit, valid_from, valid_until, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$result = $stmt->execute([
$_POST['event_id'],
strtoupper($_POST['code']),
$_POST['type'],
$_POST['discount_value'],
$_POST['usage_limit'] ?: 0,
$_POST['valid_from'],
$_POST['valid_until'],
$_POST['status']
]);
if ($result) {
addNotification('success', 'Promo code created successfully!', $user['id']);
logActivity($user['id'], 'promo_code_created', 'Created promo code: ' . $_POST['code']);
} else {
addNotification('error', 'Failed to create promo code.', $user['id']);
}
break;
case 'edit':
$stmt = $conn->prepare("UPDATE promo_codes SET event_id = ?, code = ?, type = ?, discount_value = ?, usage_limit = ?, valid_from = ?, valid_until = ?, status = ? WHERE id = ?");
$result = $stmt->execute([
$_POST['event_id'],
strtoupper($_POST['code']),
$_POST['type'],
$_POST['discount_value'],
$_POST['usage_limit'] ?: 0,
$_POST['valid_from'],
$_POST['valid_until'],
$_POST['status'],
$_POST['promo_id']
]);
if ($result) {
addNotification('success', 'Promo code updated successfully!', $user['id']);
logActivity($user['id'], 'promo_code_updated', 'Updated promo code: ' . $_POST['code']);
} else {
addNotification('error', 'Failed to update promo code.', $user['id']);
}
break;
case 'delete':
$stmt = $conn->prepare("DELETE FROM promo_codes WHERE id = ?");
$result = $stmt->execute([$_POST['promo_id']]);
if ($result) {
addNotification('success', 'Promo code deleted successfully!', $user['id']);
logActivity($user['id'], 'promo_code_deleted', 'Deleted promo code ID: ' . $_POST['promo_id']);
} else {
addNotification('error', 'Failed to delete promo code.', $user['id']);
}
break;
}
header('Location: promo-codes.php');
exit();
}
}
// Get promo codes with event information
$whereClause = "WHERE 1=1";
$params = [];
// Role-based filtering
if ($user['role'] === 'area_admin') {
$whereClause .= " AND e.area_id = ?";
$params[] = $user['area_id'];
} elseif ($user['role'] === 'district_admin') {
$whereClause .= " AND e.district_id = ?";
$params[] = $user['district_id'];
} elseif ($user['role'] === 'assembly_admin') {
$whereClause .= " AND e.assembly_id = ?";
$params[] = $user['assembly_id'];
}
$stmt = $conn->prepare("
SELECT pc.*, e.title as event_title, e.start_date, e.end_date
FROM promo_codes pc
JOIN events e ON pc.event_id = e.id
$whereClause
ORDER BY pc.valid_until DESC, pc.created_at DESC
");
$stmt->execute($params);
$promoCodes = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get events for dropdown
$eventStmt = $conn->prepare("
SELECT id, title, start_date, end_date
FROM events
$whereClause
ORDER BY start_date DESC
");
$eventStmt->execute($params);
$events = $eventStmt->fetchAll(PDO::FETCH_ASSOC);
$notifications = getNotifications($user['id']);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promo Codes Management - COP Madina Conference</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.5s ease-out',
'slide-up': 'slideUp 0.6s ease-out',
'scale-in': 'scaleIn 0.3s ease-out'
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' }
},
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-green-600 to-emerald-600 bg-clip-text text-transparent flex items-center">
<div class="p-2 rounded-xl bg-gradient-to-br from-green-500 to-emerald-600 mr-3">
<i class="fas fa-tags text-white"></i>
</div>
Promo Codes
</h1>
<p class="text-slate-600 mt-1">Manage discount codes for events</p>
</div>
<button @click="showCreateModal = true"
class="px-6 py-3 bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-700 hover:to-emerald-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>Create Promo Code</span>
</button>
</div>
</header>
<!-- Content -->
<main class="flex-1 overflow-y-auto p-8">
<!-- Notifications -->
<?php if (!empty($notifications)): ?>
<div class="mb-6 space-y-2">
<?php foreach ($notifications as $notification): ?>
<div class="alert alert-<?php echo $notification['type']; ?> p-4 rounded-xl border-l-4 <?php echo $notification['type'] === 'success' ? 'bg-emerald-50 border-emerald-500 text-emerald-800' : 'bg-red-50 border-red-500 text-red-800'; ?>">
<div class="flex items-center">
<i class="fas <?php echo $notification['type'] === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle'; ?> mr-3"></i>
<?php echo htmlspecialchars($notification['message']); ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- Promo Codes Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<?php foreach ($promoCodes as $index => $promo): ?>
<?php
$isExpired = strtotime($promo['valid_until']) < time();
$isActive = $promo['status'] === 'active' && !$isExpired;
$usagePercent = $promo['usage_limit'] > 0 ? ($promo['used_count'] / $promo['usage_limit']) * 100 : 0;
?>
<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-green-300/50 animate-slide-up <?php echo $isExpired ? 'opacity-75' : ''; ?>"
style="animation-delay: <?php echo $index * 0.1; ?>s">
<div class="flex items-start justify-between mb-4">
<div class="flex-1">
<div class="flex items-center space-x-2 mb-2">
<h3 class="text-xl font-bold text-slate-800"><?php echo htmlspecialchars($promo['code']); ?></h3>
<?php if ($isExpired): ?>
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-800">
Expired
</span>
<?php elseif ($isActive): ?>
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-emerald-100 text-emerald-800">
Active
</span>
<?php else: ?>
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-slate-100 text-slate-800">
Inactive
</span>
<?php endif; ?>
</div>
<p class="text-sm text-slate-600 mb-2"><?php echo htmlspecialchars($promo['event_title']); ?></p>
</div>
<div class="flex space-x-2">
<button @click="editPromo(<?php echo htmlspecialchars(json_encode($promo)); ?>)" class="text-blue-600 hover:text-blue-800 p-2 rounded-lg hover:bg-blue-50 transition-colors">
<i class="fas fa-edit"></i>
</button>
<button @click="deletePromo(<?php echo $promo['id']; ?>)" class="text-red-600 hover:text-red-800 p-2 rounded-lg hover:bg-red-50 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="space-y-3">
<div class="flex justify-between items-center">
<span class="text-sm text-slate-600">Discount:</span>
<span class="font-semibold text-slate-800">
<?php if ($promo['type'] === 'percentage'): ?>
<?php echo $promo['discount_value']; ?>%
<?php else: ?>
GH₵<?php echo number_format($promo['discount_value'], 2); ?>
<?php endif; ?>
</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm text-slate-600">Usage:</span>
<span class="font-semibold text-slate-800">
<?php echo $promo['used_count']; ?><?php echo $promo['usage_limit'] > 0 ? ' / ' . $promo['usage_limit'] : ' (Unlimited)'; ?>
</span>
</div>
<?php if ($promo['usage_limit'] > 0): ?>
<div class="w-full bg-slate-200 rounded-full h-2">
<div class="bg-gradient-to-r from-blue-500 to-purple-600 h-2 rounded-full transition-all duration-300" style="width: <?php echo min($usagePercent, 100); ?>%"></div>
</div>
<?php endif; ?>
<div class="pt-3 border-t border-slate-200 space-y-2">
<div class="flex justify-between items-center text-sm">
<span class="text-slate-600">Valid From:</span>
<span class="text-slate-800"><?php echo date('M j, Y', strtotime($promo['valid_from'])); ?></span>
</div>
<div class="flex justify-between items-center text-sm">
<span class="text-slate-600">Valid Until:</span>
<span class="text-slate-800 <?php echo $isExpired ? 'text-red-600' : ''; ?>">
<?php echo date('M j, Y', strtotime($promo['valid_until'])); ?>
</span>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if (empty($promoCodes)): ?>
<div class="text-center py-12">
<div class="inline-flex items-center justify-center w-16 h-16 bg-slate-100 rounded-full mb-4">
<i class="fas fa-tags text-2xl text-slate-400"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-2">No Promo Codes Found</h3>
<p class="text-slate-600 mb-6">Create your first promo code to offer discounts to your attendees.</p>
<button @click="showCreateModal = true" class="bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-700 hover:to-emerald-700 text-white px-6 py-3 rounded-xl font-semibold shadow-lg hover:shadow-xl transition-all duration-300">
<i class="fas fa-plus mr-2"></i>Create Promo Code
</button>
</div>
<?php endif; ?>
<!-- Create/Edit Modal -->
<div v-if="showCreateModal || showEditModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" @click.self="closeModals">
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto">
<div class="p-6 border-b border-slate-200">
<h2 class="text-2xl font-bold text-slate-800">{{ showEditModal ? 'Edit Promo Code' : 'Create New Promo Code' }}</h2>
</div>
<form :action="'promo-codes.php'" method="POST" class="p-6 space-y-6">
<input type="hidden" name="action" :value="showEditModal ? 'edit' : 'add'">
<input v-if="showEditModal" type="hidden" name="promo_id" :value="editingPromo.id">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Event *</label>
<select name="event_id" v-model="formData.event_id" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent">
<option value="">Select 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">Promo Code *</label>
<input type="text" name="code" v-model="formData.code" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent uppercase" placeholder="e.g., EARLY2024, SAVE20">
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Discount Type *</label>
<select name="type" v-model="formData.type" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent">
<option value="percentage">Percentage (%)</option>
<option value="fixed_amount">Fixed Amount (GH₵)</option>
</select>
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Discount Value *</label>
<input type="number" name="discount_value" v-model="formData.discount_value" step="0.01" min="0" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent" :placeholder="formData.type === 'percentage' ? 'e.g., 20' : 'e.g., 50.00'">
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Usage Limit</label>
<input type="number" name="usage_limit" v-model="formData.usage_limit" min="0" class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="0 = Unlimited">
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Valid From *</label>
<input type="datetime-local" name="valid_from" v-model="formData.valid_from" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Valid Until *</label>
<input type="datetime-local" name="valid_until" v-model="formData.valid_until" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent">
</div>
</div>
<div>
<label class="block text-sm font-semibold text-slate-700 mb-2">Status *</label>
<select name="status" v-model="formData.status" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-transparent">
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
</div>
<div class="flex justify-end space-x-4 pt-6 border-t border-slate-200">
<button type="button" @click="closeModals" class="px-6 py-3 border border-slate-300 text-slate-700 rounded-xl hover:bg-slate-50 font-semibold transition-colors">
Cancel
</button>
<button type="submit" class="px-6 py-3 bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-700 hover:to-emerald-700 text-white rounded-xl font-semibold shadow-lg hover:shadow-xl transition-all duration-300">
{{ showEditModal ? 'Update Promo Code' : 'Create Promo Code' }}
</button>
</div>
</form>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div v-if="showDeleteModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-md mx-4">
<div class="p-6 text-center">
<div class="inline-flex items-center justify-center w-16 h-16 bg-red-100 rounded-full mb-4">
<i class="fas fa-exclamation-triangle text-2xl text-red-600"></i>
</div>
<h3 class="text-xl font-bold text-slate-800 mb-2">Delete Promo Code</h3>
<p class="text-slate-600 mb-6">Are you sure you want to delete this promo code? This action cannot be undone.</p>
<div class="flex justify-center space-x-4">
<button @click="showDeleteModal = false" class="px-6 py-3 border border-slate-300 text-slate-700 rounded-xl hover:bg-slate-50 font-semibold transition-colors">
Cancel
</button>
<form method="POST" class="inline">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="promo_id" :value="deletingPromoId">
<button type="submit" class="px-6 py-3 bg-red-600 hover:bg-red-700 text-white rounded-xl font-semibold transition-colors">
Delete
</button>
</form>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
showCreateModal: false,
showEditModal: false,
showDeleteModal: false,
editingPromo: {},
deletingPromoId: null,
formData: {
event_id: '',
code: '',
type: 'percentage',
discount_value: '',
usage_limit: '',
valid_from: '',
valid_until: '',
status: 'active'
}
}
},
methods: {
editPromo(promo) {
this.editingPromo = promo;
this.formData = {
event_id: promo.event_id,
code: promo.code,
type: promo.type,
discount_value: promo.discount_value,
usage_limit: promo.usage_limit || '',
valid_from: promo.valid_from.replace(' ', 'T'),
valid_until: promo.valid_until.replace(' ', 'T'),
status: promo.status
};
this.showEditModal = true;
},
deletePromo(promoId) {
this.deletingPromoId = promoId;
this.showDeleteModal = true;
},
closeModals() {
this.showCreateModal = false;
this.showEditModal = false;
this.showDeleteModal = false;
this.editingPromo = {};
this.deletingPromoId = null;
this.formData = {
event_id: '',
code: '',
type: 'percentage',
discount_value: '',
usage_limit: '',
valid_from: '',
valid_until: '',
status: 'active'
};
}
}
}).mount('#app');
</script>
</body>
</html>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists