Sindbad~EG File Manager
<?php
require_once '../../config/config.php';
checkLogin();
$pageTitle = "Member Codes - " . APP_NAME;
$db = Database::getInstance()->getConnection();
$success = '';
$error = '';
// Get user access information
$accessLevel = $_SESSION['access_level'] ?? 'assembly';
$userAreaId = $_SESSION['area_id'] ?? null;
$userDistrictId = $_SESSION['district_id'] ?? null;
$userAssemblyId = $_SESSION['assembly_id'] ?? null;
// Function to generate unique tracking code
function generateTrackingCode($type = 'member') {
$prefix = $type === 'member' ? 'MEM' : 'USR';
return $prefix . date('Y') . str_pad(mt_rand(1, 999999), 6, '0', STR_PAD_LEFT);
}
// Function to generate barcode (Code 128)
function generateBarcode($code) {
// Check if GD is available
if (!extension_loaded('gd')) {
// Fallback to SVG with better encoding
return generateBarcodeSVG($code);
}
try {
// Create PNG image using GD
$width = 200;
$height = 50;
$image = imagecreate($width, $height);
if (!$image) {
return generateBarcodeSVG($code);
}
// Colors
$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
// Fill background
imagefill($image, 0, 0, $white);
// Generate barcode pattern based on code
$codeLength = strlen($code);
$x = 10;
for ($i = 0; $i < $codeLength && $x < 180; $i++) {
$char = ord($code[$i]);
$pattern = $char % 4; // Simple pattern based on character
for ($j = 0; $j < 3 && $x < 180; $j++) {
$barWidth = ($pattern & (1 << $j)) ? 3 : 1;
imagefilledrectangle($image, $x, 5, $x + $barWidth, 35, $black);
$x += $barWidth + 1;
}
}
// Add text
imagestring($image, 2, 10, 37, substr($code, 0, 20), $black);
// Convert to base64
ob_start();
imagepng($image);
$imageData = ob_get_contents();
ob_end_clean();
imagedestroy($image);
if ($imageData) {
return "data:image/png;base64," . base64_encode($imageData);
} else {
return generateBarcodeSVG($code);
}
} catch (Exception $e) {
return generateBarcodeSVG($code);
}
}
// Fallback SVG barcode generation
function generateBarcodeSVG($code) {
$svg = '<?xml version="1.0" encoding="UTF-8"?>
<svg width="200" height="50" xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="50" fill="white"/>
<g fill="black">';
$x = 10;
$codeLength = strlen($code);
for ($i = 0; $i < $codeLength && $x < 180; $i++) {
$char = ord($code[$i]);
$pattern = $char % 4;
for ($j = 0; $j < 3 && $x < 180; $j++) {
$barWidth = ($pattern & (1 << $j)) ? 3 : 1;
$svg .= '<rect x="' . $x . '" y="5" width="' . $barWidth . '" height="30"/>';
$x += $barWidth + 1;
}
}
$svg .= '</g>
<text x="100" y="45" text-anchor="middle" font-family="monospace" font-size="8" fill="black">' . htmlspecialchars(substr($code, 0, 20)) . '</text>
</svg>';
return "data:image/svg+xml;base64," . base64_encode($svg);
}
// Function to generate QR code (placeholder - in production use a QR library)
function generateQRCode($code) {
// Check if GD is available
if (!extension_loaded('gd')) {
return generateQRCodeSVG($code);
}
try {
// Create PNG image using GD
$size = 100;
$image = imagecreate($size, $size);
if (!$image) {
return generateQRCodeSVG($code);
}
// Colors
$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
// Fill background
imagefill($image, 0, 0, $white);
// Generate QR-like pattern based on code
$codeHash = md5($code);
$blockSize = 5;
// Corner markers
imagefilledrectangle($image, 5, 5, 30, 30, $black);
imagefilledrectangle($image, 70, 5, 95, 30, $black);
imagefilledrectangle($image, 5, 70, 30, 95, $black);
// Inner corner markers (white)
imagefilledrectangle($image, 10, 10, 25, 25, $white);
imagefilledrectangle($image, 75, 10, 90, 25, $white);
imagefilledrectangle($image, 10, 75, 25, 90, $white);
// Center dots
imagefilledrectangle($image, 15, 15, 20, 20, $black);
imagefilledrectangle($image, 80, 15, 85, 20, $black);
imagefilledrectangle($image, 15, 80, 20, 85, $black);
// Data pattern based on hash
for ($i = 0; $i < 32; $i++) {
$hexChar = hexdec($codeHash[$i]);
for ($bit = 0; $bit < 4; $bit++) {
if ($hexChar & (1 << $bit)) {
$x = 35 + (($i * 2 + $bit) % 6) * $blockSize;
$y = 35 + floor(($i * 2 + $bit) / 6) * $blockSize;
if ($x < 95 && $y < 95) {
imagefilledrectangle($image, $x, $y, $x + $blockSize - 1, $y + $blockSize - 1, $black);
}
}
}
}
// Convert to base64
ob_start();
imagepng($image);
$imageData = ob_get_contents();
ob_end_clean();
imagedestroy($image);
if ($imageData) {
return "data:image/png;base64," . base64_encode($imageData);
} else {
return generateQRCodeSVG($code);
}
} catch (Exception $e) {
return generateQRCodeSVG($code);
}
}
// Fallback SVG QR code generation
function generateQRCodeSVG($code) {
$codeHash = md5($code);
$svg = '<?xml version="1.0" encoding="UTF-8"?>
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" fill="white"/>
<g fill="black">
<!-- Corner markers -->
<rect x="5" y="5" width="25" height="25"/>
<rect x="70" y="5" width="25" height="25"/>
<rect x="5" y="70" width="25" height="25"/>
<!-- Inner corner markers -->
<rect x="10" y="10" width="15" height="15" fill="white"/>
<rect x="75" y="10" width="15" height="15" fill="white"/>
<rect x="10" y="75" width="15" height="15" fill="white"/>
<rect x="15" y="15" width="5" height="5" fill="black"/>
<rect x="80" y="15" width="5" height="5" fill="black"/>
<rect x="15" y="80" width="5" height="5" fill="black"/>';
// Data pattern based on hash
for ($i = 0; $i < 16; $i++) {
$hexChar = hexdec($codeHash[$i]);
for ($bit = 0; $bit < 4; $bit++) {
if ($hexChar & (1 << $bit)) {
$x = 35 + (($i * 2 + $bit) % 6) * 5;
$y = 35 + floor(($i * 2 + $bit) / 6) * 5;
if ($x < 95 && $y < 95) {
$svg .= '<rect x="' . $x . '" y="' . $y . '" width="5" height="5"/>';
}
}
}
}
$svg .= '</g></svg>';
return "data:image/svg+xml;base64," . base64_encode($svg);
}
// Handle form submission for User Codes
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create_user_code') {
try {
$userId = $_POST['user_id'];
$eventId = $_POST['event_id'] ?: null;
$description = sanitize($_POST['description']);
$expiresAt = $_POST['expires_at'] ?: null;
$maxUsage = $_POST['max_usage'] ?: null;
if (empty($userId)) {
throw new Exception("Please select a user");
}
// Generate unique codes
do {
$trackingCode = generateTrackingCode('user');
$stmt = $db->prepare("SELECT id FROM memberuser_codes WHERE tracking_code = :code");
$stmt->execute(['code' => $trackingCode]);
} while ($stmt->fetch());
$code = 'UC' . date('Ymd') . str_pad($userId, 4, '0', STR_PAD_LEFT) . mt_rand(100, 999);
$barcode = generateBarcode($trackingCode);
$qrcode = generateQRCode($trackingCode);
// Insert user code
$stmt = $db->prepare("
INSERT INTO memberuser_codes (
code, description, user_id, event_id, code_type, tracking_code,
barcode, qrcode, created_by, expires_at, max_usage
) VALUES (
:code, :description, :user_id, :event_id, 'user', :tracking_code,
:barcode, :qrcode, :created_by, :expires_at, :max_usage
)
");
$stmt->execute([
'code' => $code,
'description' => $description,
'user_id' => $userId,
'event_id' => $eventId,
'tracking_code' => $trackingCode,
'barcode' => $barcode,
'qrcode' => $qrcode,
'created_by' => $_SESSION['user_id'],
'expires_at' => $expiresAt,
'max_usage' => $maxUsage
]);
$success = "User code generated successfully! Tracking Code: " . $trackingCode;
// Log the action
$auditLog = new AuditLog();
$auditLog->log($_SESSION['user_id'], 'create', 'memberuser_codes', $db->lastInsertId());
} catch (Exception $e) {
$error = "Error: " . $e->getMessage();
}
}
// Handle form submission for Member Codes
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create_member_code') {
try {
$memberId = $_POST['member_id'];
$eventId = $_POST['event_id'] ?: null;
$description = sanitize($_POST['description']);
$expiresAt = $_POST['expires_at'] ?: null;
$maxUsage = $_POST['max_usage'] ?: null;
if (empty($memberId)) {
throw new Exception("Please select a member");
}
// Generate unique codes
do {
$trackingCode = generateTrackingCode('member');
$stmt = $db->prepare("SELECT id FROM memberuser_codes WHERE tracking_code = :code");
$stmt->execute(['code' => $trackingCode]);
} while ($stmt->fetch());
$code = 'MC' . date('Ymd') . str_pad($memberId, 4, '0', STR_PAD_LEFT) . mt_rand(100, 999);
$barcode = generateBarcode($trackingCode);
$qrcode = generateQRCode($trackingCode);
// Check if barcode and qrcode were generated successfully
if (empty($barcode) || empty($qrcode)) {
error_log("Warning: Barcode or QR Code generation failed for tracking code: " . $trackingCode);
}
// Insert member code
$stmt = $db->prepare("
INSERT INTO memberuser_codes (
code, description, member_id, event_id, code_type, tracking_code,
barcode, qrcode, created_by, expires_at, max_usage
) VALUES (
:code, :description, :member_id, :event_id, 'member', :tracking_code,
:barcode, :qrcode, :created_by, :expires_at, :max_usage
)
");
$stmt->execute([
'code' => $code,
'description' => $description,
'member_id' => $memberId,
'event_id' => $eventId,
'tracking_code' => $trackingCode,
'barcode' => $barcode,
'qrcode' => $qrcode,
'created_by' => $_SESSION['user_id'],
'expires_at' => $expiresAt,
'max_usage' => $maxUsage
]);
$success = "Member code generated successfully! Tracking Code: " . $trackingCode;
// Log the action
$auditLog = new AuditLog();
$auditLog->log($_SESSION['user_id'], 'create', 'memberuser_codes', $db->lastInsertId());
} catch (Exception $e) {
$error = "Error: " . $e->getMessage();
}
}
// Handle delete request
if (isset($_GET['delete']) && !empty($_GET['delete'])) {
try {
$codeId = (int)$_GET['delete'];
$codeType = $_GET['type'] ?? 'member';
$stmt = $db->prepare("DELETE FROM memberuser_codes WHERE id = :id AND code_type = :type");
$stmt->execute(['id' => $codeId, 'type' => $codeType]);
$auditLog = new AuditLog();
$auditLog->log($_SESSION['user_id'], 'delete', 'memberuser_codes', $codeId);
$success = ucfirst($codeType) . " code deleted successfully!";
} catch (Exception $e) {
$error = "Error deleting code: " . $e->getMessage();
}
}
// Check if memberuser_codes table exists
try {
$db->query("SELECT 1 FROM memberuser_codes LIMIT 1");
} catch (Exception $e) {
$error = "The memberuser_codes table does not exist. Please run the database setup first.";
}
// Get members based on access level
$members = [];
try {
// Check if membershipcard_id column exists
$columnExists = false;
try {
$checkStmt = $db->query("SHOW COLUMNS FROM members LIKE 'membershipcard_id'");
$columnExists = $checkStmt->fetch() !== false;
} catch (PDOException $e) {
// Column doesn't exist
}
$memberIdField = $columnExists ? 'm.membershipcard_id' : 'CONCAT("MC", YEAR(CURDATE()), LPAD(m.id, 6, "0")) as membershipcard_id';
$membersQuery = "
SELECT m.id, {$memberIdField}, m.first_name, m.last_name";
// Try to join with location tables, but handle if they don't exist
try {
$db->query("SELECT 1 FROM areas LIMIT 1");
$db->query("SELECT 1 FROM districts LIMIT 1");
$db->query("SELECT 1 FROM assemblies LIMIT 1");
$membersQuery .= ", a.area_name, d.district_name, asm.assembly_name
FROM members m
LEFT JOIN areas a ON m.area_id = a.id
LEFT JOIN districts d ON m.district_id = d.id
LEFT JOIN assemblies asm ON m.assembly_id = asm.id";
} catch (Exception $e) {
$membersQuery .= ", 'N/A' as area_name, 'N/A' as district_name, 'N/A' as assembly_name
FROM members m";
}
$membersQuery .= " WHERE m.is_active = 1";
$params = [];
if ($accessLevel === 'assembly' && $userAssemblyId) {
$membersQuery .= " AND m.assembly_id = :assembly_id";
$params['assembly_id'] = $userAssemblyId;
} elseif ($accessLevel === 'district' && $userDistrictId) {
$membersQuery .= " AND m.district_id = :district_id";
$params['district_id'] = $userDistrictId;
} elseif ($accessLevel === 'area' && $userAreaId) {
$membersQuery .= " AND m.area_id = :area_id";
$params['area_id'] = $userAreaId;
}
$membersQuery .= " ORDER BY m.first_name, m.last_name";
$stmt = $db->prepare($membersQuery);
$stmt->execute($params);
$members = $stmt->fetchAll();
} catch (Exception $e) {
$error = "Error loading members: " . $e->getMessage();
}
// Get users based on access level
$users = [];
try {
$usersQuery = "
SELECT u.id, u.full_name, u.username, u.email, u.access_level";
// Try to join with location tables, but handle if they don't exist
try {
$db->query("SELECT 1 FROM areas LIMIT 1");
$usersQuery .= ", a.area_name, d.district_name, asm.assembly_name
FROM users u
LEFT JOIN areas a ON u.area_id = a.id
LEFT JOIN districts d ON u.district_id = d.id
LEFT JOIN assemblies asm ON u.assembly_id = asm.id";
} catch (Exception $e) {
$usersQuery .= ", 'N/A' as area_name, 'N/A' as district_name, 'N/A' as assembly_name
FROM users u";
}
$usersQuery .= " WHERE u.is_active = 1";
$userParams = [];
if ($accessLevel === 'assembly' && $userAssemblyId) {
$usersQuery .= " AND u.assembly_id = :assembly_id";
$userParams['assembly_id'] = $userAssemblyId;
} elseif ($accessLevel === 'district' && $userDistrictId) {
$usersQuery .= " AND u.district_id = :district_id";
$userParams['district_id'] = $userDistrictId;
} elseif ($accessLevel === 'area' && $userAreaId) {
$usersQuery .= " AND u.area_id = :area_id";
$userParams['area_id'] = $userAreaId;
}
$usersQuery .= " ORDER BY u.full_name";
$stmt = $db->prepare($usersQuery);
$stmt->execute($userParams);
$users = $stmt->fetchAll();
} catch (Exception $e) {
$error = "Error loading users: " . $e->getMessage();
}
// Get events
$events = [];
try {
$eventsStmt = $db->query("SELECT id, name FROM events WHERE is_active = 1 ORDER BY name");
$events = $eventsStmt->fetchAll();
} catch (Exception $e) {
// Events table doesn't exist, use empty array
$events = [];
}
// Get member codes
$memberCodes = [];
try {
$codesQuery = "
SELECT mc.id, mc.code, mc.description, mc.tracking_code, mc.barcode, mc.qrcode,
mc.is_active, mc.created_at, mc.expires_at, mc.max_usage, mc.usage_count,
m.first_name, m.last_name";
// Add membershipcard_id if column exists
if ($columnExists) {
$codesQuery .= ", m.membershipcard_id";
} else {
$codesQuery .= ", CONCAT('MC', YEAR(CURDATE()), LPAD(m.id, 6, '0')) as membershipcard_id";
}
$codesQuery .= ", e.name as event_name, u.full_name as created_by_name
FROM memberuser_codes mc
JOIN members m ON mc.member_id = m.id
LEFT JOIN events e ON mc.event_id = e.id
LEFT JOIN users u ON mc.created_by = u.id
WHERE mc.code_type = 'member'";
// Apply same access restrictions
if ($accessLevel === 'assembly' && $userAssemblyId) {
$codesQuery .= " AND m.assembly_id = :assembly_id";
} elseif ($accessLevel === 'district' && $userDistrictId) {
$codesQuery .= " AND m.district_id = :district_id";
} elseif ($accessLevel === 'area' && $userAreaId) {
$codesQuery .= " AND m.area_id = :area_id";
}
$codesQuery .= " ORDER BY mc.created_at DESC";
$stmt = $db->prepare($codesQuery);
$stmt->execute($params);
$memberCodes = $stmt->fetchAll();
} catch (Exception $e) {
// Table doesn't exist or other error
$memberCodes = [];
}
// Get user codes
$userCodes = [];
try {
$userCodesQuery = "
SELECT mc.id, mc.code, mc.description, mc.tracking_code, mc.barcode, mc.qrcode,
mc.is_active, mc.created_at, mc.expires_at, mc.max_usage, mc.usage_count,
u.full_name, u.username, u.email,
e.name as event_name, uc.full_name as created_by_name
FROM memberuser_codes mc
JOIN users u ON mc.user_id = u.id
LEFT JOIN events e ON mc.event_id = e.id
LEFT JOIN users uc ON mc.created_by = uc.id
WHERE mc.code_type = 'user'";
// Apply same access restrictions for user codes
if ($accessLevel === 'assembly' && $userAssemblyId) {
$userCodesQuery .= " AND u.assembly_id = :assembly_id";
} elseif ($accessLevel === 'district' && $userDistrictId) {
$userCodesQuery .= " AND u.district_id = :district_id";
} elseif ($accessLevel === 'area' && $userAreaId) {
$userCodesQuery .= " AND u.area_id = :area_id";
}
$userCodesQuery .= " ORDER BY mc.created_at DESC";
$stmt = $db->prepare($userCodesQuery);
$stmt->execute($userParams);
$userCodes = $stmt->fetchAll();
} catch (Exception $e) {
// Table doesn't exist or other error
$userCodes = [];
}
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-7xl mx-auto">
<!-- Header -->
<div class="flex justify-between items-center mb-6">
<div>
<h1 class="text-3xl font-bold text-gray-800">
<i class="fas fa-qrcode mr-2 text-blue-500"></i>Member Codes
</h1>
<p class="text-gray-600 mt-2">Generate and manage tracking codes for members</p>
</div>
</div>
<!-- Tab Navigation -->
<div class="bg-white rounded-xl shadow-lg mb-6">
<div class="border-b border-gray-200">
<nav class="-mb-px flex space-x-8 px-6">
<button id="memberCodesTab" onclick="showTab('memberCodes')"
class="py-4 px-1 border-b-2 border-blue-500 font-medium text-sm text-blue-600">
<i class="fas fa-users mr-2"></i>Member Codes
</button>
<button id="userCodesTab" onclick="showTab('userCodes')"
class="py-4 px-1 border-b-2 border-transparent font-medium text-sm text-gray-500 hover:text-gray-700 hover:border-gray-300">
<i class="fas fa-user-cog mr-2"></i>User Special Codes
</button>
</nav>
</div>
</div>
<!-- Success/Error Messages -->
<?php if (!empty($success)): ?>
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6 flex items-center">
<i class="fas fa-check-circle mr-2"></i>
<span><?php echo $success; ?></span>
</div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6 flex items-center">
<i class="fas fa-exclamation-circle mr-2"></i>
<span><?php echo $error; ?></span>
</div>
<?php endif; ?>
<!-- Member Codes Tab Content -->
<div id="memberCodesContent" class="tab-content">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Generate Code Form -->
<div class="lg:col-span-1">
<div class="bg-white rounded-xl shadow-lg p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-6">
<i class="fas fa-plus-circle mr-2 text-green-500"></i>Generate Member Code
</h2>
<form method="POST" action="" class="space-y-4">
<input type="hidden" name="action" value="create_member_code">
<div>
<label for="member_id" class="block text-sm font-medium text-gray-700 mb-2">Select Member *</label>
<select name="member_id" id="member_id" required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Choose Member</option>
<?php foreach ($members as $member): ?>
<option value="<?php echo $member['id']; ?>">
<?php echo htmlspecialchars($member['first_name'] . ' ' . $member['last_name']); ?>
<?php if (!empty($member['membershipcard_id'])): ?>
(<?php echo htmlspecialchars($member['membershipcard_id']); ?>)
<?php endif; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="event_id" class="block text-sm font-medium text-gray-700 mb-2">Event Type</label>
<select name="event_id" id="event_id"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">General Code</option>
<?php foreach ($events as $event): ?>
<option value="<?php echo $event['id']; ?>">
<?php echo htmlspecialchars($event['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="description" class="block text-sm font-medium text-gray-700 mb-2">Description</label>
<textarea name="description" id="description" rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Purpose of this code..."></textarea>
</div>
<div>
<label for="expires_at" class="block text-sm font-medium text-gray-700 mb-2">Expires At</label>
<input type="datetime-local" name="expires_at" id="expires_at"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="max_usage" class="block text-sm font-medium text-gray-700 mb-2">Max Usage</label>
<input type="number" name="max_usage" id="max_usage" min="1"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Leave empty for unlimited">
</div>
<button type="submit"
class="w-full bg-blue-500 text-white py-2 px-4 rounded-lg hover:bg-blue-600 transition">
<i class="fas fa-qrcode mr-2"></i>Generate Code
</button>
</form>
</div>
</div>
<!-- Member Codes List -->
<div class="lg:col-span-2">
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-800">
<i class="fas fa-list mr-2 text-blue-500"></i>Generated Member Codes
</h2>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<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">Event</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Usage</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</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 if (empty($memberCodes)): ?>
<tr>
<td colspan="6" class="px-6 py-12 text-center text-gray-500">
<i class="fas fa-qrcode text-4xl mb-4"></i>
<p>No member codes generated yet.</p>
</td>
</tr>
<?php else: ?>
<?php foreach ($memberCodes as $code): ?>
<tr class="hover:bg-gray-50 transition">
<td class="px-6 py-4">
<div class="font-semibold text-gray-800">
<?php echo htmlspecialchars($code['first_name'] . ' ' . $code['last_name']); ?>
</div>
<div class="text-sm text-gray-500">
ID: <?php echo htmlspecialchars($code['membershipcard_id'] ?? 'N/A'); ?>
</div>
</td>
<td class="px-6 py-4">
<div class="font-mono text-sm bg-blue-100 text-blue-800 px-2 py-1 rounded">
<?php echo htmlspecialchars($code['tracking_code']); ?>
</div>
<div class="text-xs text-gray-500 mt-1">
<?php echo htmlspecialchars($code['code']); ?>
</div>
</td>
<td class="px-6 py-4 text-sm">
<?php if ($code['event_name']): ?>
<span class="text-gray-800"><?php echo htmlspecialchars($code['event_name']); ?></span>
<?php else: ?>
<span class="text-gray-400">General</span>
<?php endif; ?>
</td>
<td class="px-6 py-4 text-sm">
<div class="text-gray-800">
<?php echo $code['usage_count']; ?>
<?php if ($code['max_usage']): ?>
/ <?php echo $code['max_usage']; ?>
<?php else: ?>
/ ∞
<?php endif; ?>
</div>
</td>
<td class="px-6 py-4">
<?php
$isExpired = $code['expires_at'] && strtotime($code['expires_at']) < time();
$isMaxedOut = $code['max_usage'] && $code['usage_count'] >= $code['max_usage'];
?>
<?php if (!$code['is_active']): ?>
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-gray-100 text-gray-800">
Inactive
</span>
<?php elseif ($isExpired): ?>
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
Expired
</span>
<?php elseif ($isMaxedOut): ?>
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-orange-100 text-orange-800">
Max Used
</span>
<?php else: ?>
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
Active
</span>
<?php endif; ?>
</td>
<td class="px-6 py-4 text-sm">
<div class="flex space-x-2">
<button onclick="viewCode(<?php echo htmlspecialchars(json_encode($code)); ?>)"
class="text-blue-600 hover:text-blue-800 transition"
title="View Codes">
<i class="fas fa-eye"></i>
</button>
<button onclick="deleteCode(<?php echo $code['id']; ?>, '<?php echo htmlspecialchars($code['tracking_code']); ?>')"
class="text-red-600 hover:text-red-800 transition"
title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- User Codes Tab Content -->
<div id="userCodesContent" class="tab-content hidden">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Generate User Code Form -->
<div class="lg:col-span-1">
<div class="bg-white rounded-xl shadow-lg p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-6">
<i class="fas fa-plus-circle mr-2 text-green-500"></i>Generate User Code
</h2>
<form method="POST" action="" class="space-y-4">
<input type="hidden" name="action" value="create_user_code">
<div>
<label for="user_id" class="block text-sm font-medium text-gray-700 mb-2">Select User *</label>
<select name="user_id" id="user_id" required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Choose User</option>
<?php foreach ($users as $user): ?>
<option value="<?php echo $user['id']; ?>">
<?php echo htmlspecialchars($user['full_name']); ?>
(<?php echo htmlspecialchars($user['username']); ?>)
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="user_event_id" class="block text-sm font-medium text-gray-700 mb-2">Event Type</label>
<select name="event_id" id="user_event_id"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">General Code</option>
<?php foreach ($events as $event): ?>
<option value="<?php echo $event['id']; ?>">
<?php echo htmlspecialchars($event['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="user_description" class="block text-sm font-medium text-gray-700 mb-2">Description</label>
<textarea name="description" id="user_description" rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Purpose of this code..."></textarea>
</div>
<div>
<label for="user_expires_at" class="block text-sm font-medium text-gray-700 mb-2">Expires At</label>
<input type="datetime-local" name="expires_at" id="user_expires_at"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="user_max_usage" class="block text-sm font-medium text-gray-700 mb-2">Max Usage</label>
<input type="number" name="max_usage" id="user_max_usage" min="1"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Leave empty for unlimited">
</div>
<button type="submit"
class="w-full bg-purple-500 text-white py-2 px-4 rounded-lg hover:bg-purple-600 transition">
<i class="fas fa-qrcode mr-2"></i>Generate User Code
</button>
</form>
</div>
</div>
<!-- User Codes List -->
<div class="lg:col-span-2">
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-800">
<i class="fas fa-list mr-2 text-purple-500"></i>Generated User Codes
</h2>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">User</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">Event</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Usage</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</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 if (empty($userCodes)): ?>
<tr>
<td colspan="6" class="px-6 py-12 text-center text-gray-500">
<i class="fas fa-user-cog text-4xl mb-4"></i>
<p>No user codes generated yet.</p>
</td>
</tr>
<?php else: ?>
<?php foreach ($userCodes as $code): ?>
<tr class="hover:bg-gray-50 transition">
<td class="px-6 py-4">
<div class="font-semibold text-gray-800">
<?php echo htmlspecialchars($code['full_name']); ?>
</div>
<div class="text-sm text-gray-500">
@<?php echo htmlspecialchars($code['username']); ?>
</div>
</td>
<td class="px-6 py-4">
<div class="font-mono text-sm bg-purple-100 text-purple-800 px-2 py-1 rounded">
<?php echo htmlspecialchars($code['tracking_code']); ?>
</div>
<div class="text-xs text-gray-500 mt-1">
<?php echo htmlspecialchars($code['code']); ?>
</div>
</td>
<td class="px-6 py-4 text-sm">
<?php if ($code['event_name']): ?>
<span class="text-gray-800"><?php echo htmlspecialchars($code['event_name']); ?></span>
<?php else: ?>
<span class="text-gray-400">General</span>
<?php endif; ?>
</td>
<td class="px-6 py-4 text-sm">
<div class="text-gray-800">
<?php echo $code['usage_count']; ?>
<?php if ($code['max_usage']): ?>
/ <?php echo $code['max_usage']; ?>
<?php else: ?>
/ ∞
<?php endif; ?>
</div>
</td>
<td class="px-6 py-4">
<?php
$isExpired = $code['expires_at'] && strtotime($code['expires_at']) < time();
$isMaxedOut = $code['max_usage'] && $code['usage_count'] >= $code['max_usage'];
?>
<?php if (!$code['is_active']): ?>
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-gray-100 text-gray-800">
Inactive
</span>
<?php elseif ($isExpired): ?>
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
Expired
</span>
<?php elseif ($isMaxedOut): ?>
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-orange-100 text-orange-800">
Max Used
</span>
<?php else: ?>
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
Active
</span>
<?php endif; ?>
</td>
<td class="px-6 py-4 text-sm">
<div class="flex space-x-2">
<button onclick="viewCode(<?php echo htmlspecialchars(json_encode($code)); ?>)"
class="text-purple-600 hover:text-purple-800 transition"
title="View Codes">
<i class="fas fa-eye"></i>
</button>
<button onclick="deleteUserCode(<?php echo $code['id']; ?>, '<?php echo htmlspecialchars($code['tracking_code']); ?>')"
class="text-red-600 hover:text-red-800 transition"
title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Code View Modal -->
<div id="codeModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white rounded-xl shadow-xl max-w-md w-full">
<div class="p-6">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-bold text-gray-800">Code Details</h2>
<button onclick="closeCodeModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<div id="codeDetails" class="space-y-4">
<!-- Code details will be populated here -->
</div>
</div>
</div>
</div>
</div>
<script>
function showTab(tabName) {
// Hide all tab contents
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.add('hidden');
});
// Remove active class from all tabs
document.querySelectorAll('[id$="Tab"]').forEach(tab => {
tab.className = tab.className.replace('border-blue-500 text-blue-600', 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300');
});
// Show selected tab content
document.getElementById(tabName + 'Content').classList.remove('hidden');
// Add active class to selected tab
const activeTab = document.getElementById(tabName + 'Tab');
activeTab.className = activeTab.className.replace('border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300', 'border-blue-500 text-blue-600');
}
function viewCode(code) {
const modal = document.getElementById('codeModal');
const details = document.getElementById('codeDetails');
// Debug: Check if barcode and qrcode data exists
console.log('Code object:', code);
console.log('Barcode length:', code.barcode ? code.barcode.length : 0);
console.log('QR Code length:', code.qrcode ? code.qrcode.length : 0);
// Create elements to avoid HTML escaping issues with base64 data
const container = document.createElement('div');
container.className = 'text-center';
// Name and ID
const nameDiv = document.createElement('div');
nameDiv.className = 'mb-4';
nameDiv.innerHTML = `
<h3 class="font-semibold text-gray-800">${escapeHtml(code.first_name)} ${escapeHtml(code.last_name)}</h3>
<p class="text-sm text-gray-500">Member ID: ${escapeHtml(code.membershipcard_id || 'N/A')}</p>
`;
container.appendChild(nameDiv);
// Tracking Code
const trackingDiv = document.createElement('div');
trackingDiv.className = 'mb-4';
trackingDiv.innerHTML = `
<label class="block text-sm font-medium text-gray-700 mb-2">Tracking Code</label>
<div class="font-mono text-lg bg-gray-100 p-2 rounded">${escapeHtml(code.tracking_code)}</div>
`;
container.appendChild(trackingDiv);
// Barcode
const barcodeDiv = document.createElement('div');
barcodeDiv.className = 'mb-4';
const barcodeLabel = document.createElement('label');
barcodeLabel.className = 'block text-sm font-medium text-gray-700 mb-2';
barcodeLabel.textContent = 'Barcode';
const barcodeImgContainer = document.createElement('div');
barcodeImgContainer.className = 'flex justify-center';
const barcodeImg = document.createElement('img');
barcodeImg.src = code.barcode || '';
barcodeImg.alt = 'Barcode';
barcodeImg.className = 'border rounded';
barcodeImg.style.maxWidth = '200px';
barcodeImg.style.height = 'auto';
barcodeImg.onerror = function() {
this.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjUwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iNTAiIGZpbGw9IiNmM2Y0ZjYiLz48dGV4dCB4PSIxMDAiIHk9IjI1IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzljYTNhZiI+QmFyY29kZSBOb3QgQXZhaWxhYmxlPC90ZXh0Pjwvc3ZnPg==';
};
barcodeImgContainer.appendChild(barcodeImg);
barcodeDiv.appendChild(barcodeLabel);
barcodeDiv.appendChild(barcodeImgContainer);
container.appendChild(barcodeDiv);
// QR Code
const qrcodeDiv = document.createElement('div');
qrcodeDiv.className = 'mb-4';
const qrcodeLabel = document.createElement('label');
qrcodeLabel.className = 'block text-sm font-medium text-gray-700 mb-2';
qrcodeLabel.textContent = 'QR Code';
const qrcodeImgContainer = document.createElement('div');
qrcodeImgContainer.className = 'flex justify-center';
const qrcodeImg = document.createElement('img');
qrcodeImg.src = code.qrcode || '';
qrcodeImg.alt = 'QR Code';
qrcodeImg.className = 'border rounded';
qrcodeImg.style.width = '100px';
qrcodeImg.style.height = '100px';
qrcodeImg.onerror = function() {
this.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2YzZjRmNiIvPjx0ZXh0IHg9IjUwIiB5PSI1MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTAiIGZpbGw9IiM5Y2EzYWYiPlFSIENvZGU8L3RleHQ+PHRleHQgeD0iNTAiIHk9IjY1IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSI4IiBmaWxsPSIjOWNhM2FmIj5Ob3QgQXZhaWxhYmxlPC90ZXh0Pjwvc3ZnPg==';
};
qrcodeImgContainer.appendChild(qrcodeImg);
qrcodeDiv.appendChild(qrcodeLabel);
qrcodeDiv.appendChild(qrcodeImgContainer);
container.appendChild(qrcodeDiv);
// Description
if (code.description) {
const descDiv = document.createElement('div');
descDiv.className = 'mb-4';
descDiv.innerHTML = `
<label class="block text-sm font-medium text-gray-700 mb-2">Description</label>
<p class="text-sm text-gray-600">${escapeHtml(code.description)}</p>
`;
container.appendChild(descDiv);
}
details.innerHTML = '';
details.appendChild(container);
modal.classList.remove('hidden');
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function closeCodeModal() {
document.getElementById('codeModal').classList.add('hidden');
}
function deleteCode(id, trackingCode) {
if (confirm(`Are you sure you want to delete the member code "${trackingCode}"?`)) {
window.location.href = `?delete=${id}&type=member`;
}
}
function deleteUserCode(id, trackingCode) {
if (confirm(`Are you sure you want to delete the user code "${trackingCode}"?`)) {
window.location.href = `?delete=${id}&type=user`;
}
}
// Close modal when clicking outside
document.getElementById('codeModal').addEventListener('click', function(e) {
if (e.target === this) {
closeCodeModal();
}
});
</script>
<?php include '../../includes/footer.php'; ?>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists