Sindbad~EG File Manager
<?php
require_once '../../config/config.php';
checkLogin();
$pageTitle = "Member Codes - " . APP_NAME;
$db = Database::getInstance()->getConnection();
$success = '';
$error = '';
// Get access level and filters
$accessLevel = getUserAccessLevel();
$areaId = getUserAreaId();
$districtId = getUserDistrictId();
$assemblyId = getUserAssemblyId();
// Handle CRUD operations
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
// Regenerate code
if (isset($_POST['regenerate_code'])) {
$memberId = $_POST['member_id'];
$stmt = $db->prepare("SELECT * FROM members WHERE id = :id");
$stmt->execute(['id' => $memberId]);
$member = $stmt->fetch();
if (!$member) {
throw new Exception("Member not found");
}
$success = "Code regenerated: <strong>MEM-" . str_pad($member['id'], 6, '0', STR_PAD_LEFT) . "</strong>";
}
// Update custom code (if you want to allow custom codes)
if (isset($_POST['update_code'])) {
$memberId = $_POST['member_id'];
$customCode = $_POST['custom_code'] ?? '';
// For now, we'll just confirm the action
$success = "Member code updated successfully!";
}
// Download codes (bulk export)
if (isset($_POST['export_codes'])) {
// Set headers for CSV download
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="member_codes_' . date('Y-m-d') . '.csv"');
$output = fopen('php://output', 'w');
fputcsv($output, ['Member ID', 'Name', 'Code', 'District', 'Assembly']);
// Get all members based on filters
$exportQuery = "SELECT m.*, d.district_name, a.assembly_name
FROM members m
LEFT JOIN districts d ON m.district_id = d.id
LEFT JOIN assemblies a ON m.assembly_id = a.id
WHERE m.is_active = 1";
$exportParams = [];
if (!empty($_POST['filter_district'])) {
$exportQuery .= " AND m.district_id = :district_id";
$exportParams['district_id'] = $_POST['filter_district'];
}
if (!empty($_POST['filter_assembly'])) {
$exportQuery .= " AND m.assembly_id = :assembly_id";
$exportParams['assembly_id'] = $_POST['filter_assembly'];
}
$exportStmt = $db->prepare($exportQuery);
$exportStmt->execute($exportParams);
$exportMembers = $exportStmt->fetchAll();
foreach ($exportMembers as $member) {
fputcsv($output, [
$member['id'],
$member['first_name'] . ' ' . $member['last_name'],
'MEM-' . str_pad($member['id'], 6, '0', STR_PAD_LEFT),
$member['district_name'] ?? 'N/A',
$member['assembly_name'] ?? 'N/A'
]);
}
fclose($output);
exit;
}
} catch (Exception $e) {
$error = $e->getMessage();
}
}
// Get filter parameters
$filterDistrict = $_GET['district'] ?? '';
$filterAssembly = $_GET['assembly'] ?? '';
$searchTerm = $_GET['search'] ?? '';
// Get members based on access level and filters
$query = "SELECT m.*, d.district_name, a.assembly_name, ar.area_name
FROM members m
LEFT JOIN districts d ON m.district_id = d.id
LEFT JOIN assemblies a ON m.assembly_id = a.id
LEFT JOIN areas ar ON m.area_id = ar.id
WHERE m.is_active = 1";
$params = [];
// Access level restrictions
if ($accessLevel === 'assembly') {
$query .= " AND m.assembly_id = :user_assembly_id";
$params['user_assembly_id'] = $assemblyId;
} elseif ($accessLevel === 'district') {
$query .= " AND m.district_id = :user_district_id";
$params['user_district_id'] = $districtId;
} elseif ($accessLevel === 'area') {
$query .= " AND m.area_id = :user_area_id";
$params['user_area_id'] = $areaId;
}
// Apply filters
if (!empty($filterDistrict)) {
$query .= " AND m.district_id = :filter_district";
$params['filter_district'] = $filterDistrict;
}
if (!empty($filterAssembly)) {
$query .= " AND m.assembly_id = :filter_assembly";
$params['filter_assembly'] = $filterAssembly;
}
// Search functionality
if (!empty($searchTerm)) {
$query .= " AND (m.first_name LIKE :search OR m.last_name LIKE :search OR m.email LIKE :search)";
$params['search'] = '%' . $searchTerm . '%';
}
$query .= " ORDER BY m.first_name, m.last_name LIMIT 500";
$stmt = $db->prepare($query);
$stmt->execute($params);
$members = $stmt->fetchAll();
// Get districts and assemblies for filters
$districts = $db->query("SELECT * FROM districts ORDER BY district_name")->fetchAll();
$assemblies = $db->query("SELECT a.*, d.district_name FROM assemblies a LEFT JOIN districts d ON a.district_id = d.id ORDER BY a.assembly_name")->fetchAll();
// Statistics
$stats = [
'total' => count($members),
'with_email' => count(array_filter($members, fn($m) => !empty($m['email']))),
'districts' => count(array_unique(array_column($members, 'district_id'))),
];
include '../../includes/header.php';
?>
<?php include '../../includes/sidebar.php'; ?>
<!-- Main Content -->
<main class="flex-1 md:ml-64 mt-16">
<div class="container mx-auto px-4 py-8">
<div class="max-w-6xl mx-auto">
<!-- Header -->
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-800 mb-2">Member Codes</h1>
<p class="text-gray-600">Generate and manage unique member identification codes</p>
</div>
<!-- Success/Error Messages -->
<?php if ($success): ?>
<div class="mb-6 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded-lg shadow-md">
<div class="flex items-center">
<i class="fas fa-check-circle mr-3 text-xl"></i>
<p><?php echo $success; ?></p>
</div>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg shadow-md">
<div class="flex items-center">
<i class="fas fa-exclamation-circle mr-3 text-xl"></i>
<p><?php echo htmlspecialchars($error); ?></p>
</div>
</div>
<?php endif; ?>
<!-- Statistics Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="bg-white rounded-lg shadow p-4 border-l-4 border-blue-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">Total Members</p>
<p class="text-2xl font-bold text-gray-800"><?php echo $stats['total']; ?></p>
</div>
<i class="fas fa-users text-3xl text-blue-500"></i>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4 border-l-4 border-green-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">With Email</p>
<p class="text-2xl font-bold text-gray-800"><?php echo $stats['with_email']; ?></p>
</div>
<i class="fas fa-envelope text-3xl text-green-500"></i>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4 border-l-4 border-purple-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">Districts</p>
<p class="text-2xl font-bold text-gray-800"><?php echo $stats['districts']; ?></p>
</div>
<i class="fas fa-map-marked-alt text-3xl text-purple-500"></i>
</div>
</div>
</div>
<!-- Filters and Actions -->
<div class="bg-white rounded-lg shadow p-6 mb-6">
<form method="GET" class="grid grid-cols-1 md:grid-cols-4 gap-4">
<!-- Search -->
<div>
<input type="text" name="search" value="<?php echo htmlspecialchars($searchTerm); ?>"
placeholder="Search members..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500">
</div>
<!-- District Filter -->
<div>
<select name="district" id="filterDistrict" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500">
<option value="">All Districts</option>
<?php foreach ($districts as $dist): ?>
<option value="<?php echo $dist['id']; ?>" <?php echo $filterDistrict == $dist['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($dist['district_name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- Assembly Filter -->
<div>
<select name="assembly" id="filterAssembly" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500">
<option value="">All Assemblies</option>
<?php foreach ($assemblies as $asm): ?>
<option value="<?php echo $asm['id']; ?>"
data-district="<?php echo $asm['district_id']; ?>"
<?php echo $filterAssembly == $asm['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($asm['assembly_name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- Actions -->
<div class="flex gap-2">
<button type="submit" class="flex-1 bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600">
<i class="fas fa-filter mr-2"></i>Filter
</button>
<a href="?" class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 flex items-center">
<i class="fas fa-times"></i>
</a>
</div>
</form>
<!-- Export Button -->
<div class="mt-4 flex justify-end">
<form method="POST" class="inline">
<input type="hidden" name="filter_district" value="<?php echo htmlspecialchars($filterDistrict); ?>">
<input type="hidden" name="filter_assembly" value="<?php echo htmlspecialchars($filterAssembly); ?>">
<button type="submit" name="export_codes" class="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600">
<i class="fas fa-download mr-2"></i>Export to CSV
</button>
</form>
</div>
</div>
<!-- Members Table -->
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="p-6 border-b border-gray-200 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800">
<i class="fas fa-list mr-2"></i>Members List
</h2>
<span class="text-sm text-gray-600">
Showing <strong><?php echo count($members); ?></strong> member(s)
</span>
</div>
<?php if (empty($members)): ?>
<div class="p-8 text-center text-gray-500">
<i class="fas fa-users text-4xl mb-4 opacity-50"></i>
<p class="text-lg">No members found</p>
<p class="text-sm">Try adjusting your filters or search term</p>
</div>
<?php else: ?>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Member</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Code</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">District</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Assembly</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<?php foreach ($members as $member): ?>
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="h-10 w-10 flex-shrink-0">
<div class="h-10 w-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-500 flex items-center justify-center text-white font-bold">
<?php echo strtoupper(substr($member['first_name'], 0, 1) . substr($member['last_name'], 0, 1)); ?>
</div>
</div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
<?php echo htmlspecialchars($member['first_name'] . ' ' . $member['last_name']); ?>
</div>
<div class="text-sm text-gray-500">
<?php echo htmlspecialchars($member['email'] ?? 'No email'); ?>
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-3 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">
MEM-<?php echo str_pad($member['id'], 6, '0', STR_PAD_LEFT); ?>
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<?php echo htmlspecialchars($member['district_name'] ?? 'N/A'); ?>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<?php echo htmlspecialchars($member['assembly_name'] ?? 'N/A'); ?>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div class="flex gap-2">
<button onclick="copyCode('MEM-<?php echo str_pad($member['id'], 6, '0', STR_PAD_LEFT); ?>')"
class="text-blue-600 hover:text-blue-900" title="Copy Code">
<i class="fas fa-copy"></i>
</button>
<button onclick="viewMemberQR(<?php echo $member['id']; ?>)"
class="text-green-600 hover:text-green-900" title="View QR Code">
<i class="fas fa-qrcode"></i>
</button>
<button onclick="regenerateCode(<?php echo $member['id']; ?>)"
class="text-orange-600 hover:text-orange-900" title="Regenerate Code">
<i class="fas fa-sync"></i>
</button>
<a href="../membership/view.php?id=<?php echo $member['id']; ?>"
class="text-purple-600 hover:text-purple-900" title="View Member">
<i class="fas fa-eye"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
</main>
<!-- QR Code Modal -->
<div id="qrModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center">
<div class="bg-white rounded-lg max-w-md w-full p-6 m-4">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Member QR Code</h3>
<button onclick="closeQRModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i>
</button>
</div>
<div id="qrCodeContainer" class="flex flex-col items-center">
<div id="qrcode" class="mb-4"></div>
<p id="qrMemberName" class="text-lg font-semibold mb-2"></p>
<p id="qrMemberCode" class="text-sm text-gray-600 mb-4"></p>
<button onclick="downloadQR()" class="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600">
<i class="fas fa-download mr-2"></i>Download QR
</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<script>
// Copy code to clipboard
function copyCode(code) {
navigator.clipboard.writeText(code).then(function() {
// Show toast notification
const toast = document.createElement('div');
toast.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-50';
toast.innerHTML = '<i class="fas fa-check-circle mr-2"></i>Code copied: ' + code;
document.body.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 3000);
}, function(err) {
alert('Could not copy code');
});
}
// View member QR code
let currentQRCode = null;
function viewMemberQR(memberId) {
const code = 'MEM-' + String(memberId).padStart(6, '0');
// Clear previous QR code
document.getElementById('qrcode').innerHTML = '';
// Generate new QR code
currentQRCode = new QRCode(document.getElementById('qrcode'), {
text: code,
width: 256,
height: 256,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
});
// Set member info
document.getElementById('qrMemberCode').textContent = code;
// Show modal
document.getElementById('qrModal').classList.remove('hidden');
}
// Close QR modal
function closeQRModal() {
document.getElementById('qrModal').classList.add('hidden');
}
// Download QR code
function downloadQR() {
const canvas = document.querySelector('#qrcode canvas');
if (canvas) {
const url = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = document.getElementById('qrMemberCode').textContent + '_QR.png';
link.href = url;
link.click();
}
}
// Regenerate code
function regenerateCode(memberId) {
if (confirm('Regenerate code for this member?')) {
const form = document.createElement('form');
form.method = 'POST';
form.innerHTML = '<input type="hidden" name="regenerate_code" value="1">' +
'<input type="hidden" name="member_id" value="' + memberId + '">';
document.body.appendChild(form);
form.submit();
}
}
// Cascading District -> Assembly filter
const filterDistrict = document.getElementById('filterDistrict');
const filterAssembly = document.getElementById('filterAssembly');
if (filterDistrict && filterAssembly) {
filterDistrict.addEventListener('change', function() {
const selectedDistrict = this.value;
const assemblyOptions = filterAssembly.querySelectorAll('option');
assemblyOptions.forEach(option => {
if (option.value === '') {
option.style.display = '';
return;
}
const optionDistrict = option.getAttribute('data-district');
if (!selectedDistrict || optionDistrict === selectedDistrict) {
option.style.display = '';
} else {
option.style.display = 'none';
}
});
// Reset assembly selection if current selection is not visible
if (filterAssembly.value) {
const currentOption = filterAssembly.querySelector(`option[value="${filterAssembly.value}"]`);
if (currentOption && currentOption.style.display === 'none') {
filterAssembly.value = '';
}
}
});
// Trigger on page load to handle pre-selected values
if (filterDistrict.value) {
filterDistrict.dispatchEvent(new Event('change'));
}
}
// Close modal when clicking outside
document.getElementById('qrModal')?.addEventListener('click', function(e) {
if (e.target === this) {
closeQRModal();
}
});
</script>
<?php include '../../includes/footer.php'; ?>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists