Sindbad~EG File Manager
<?php
require_once '../../config/config.php';
checkLogin();
$pageTitle = "Program Attendance - " . APP_NAME;
$db = Database::getInstance()->getConnection();
$success = '';
$error = '';
$programId = $_GET['program_id'] ?? null;
$selectedDate = $_GET['date'] ?? date('Y-m-d');
$filterDistrict = $_GET['filter_district'] ?? '';
$filterAssembly = $_GET['filter_assembly'] ?? '';
if (!$programId) {
$_SESSION['error'] = 'Please select a program to mark attendance';
header('Location: index.php');
exit();
}
// Get program details
$stmt = $db->prepare("SELECT * FROM programs WHERE id = :id");
$stmt->execute(['id' => $programId]);
$program = $stmt->fetch();
if (!$program) {
redirect('index.php');
}
// Handle attendance marking
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['mark_attendance'])) {
try {
$db->beginTransaction();
foreach ($_POST['attendance'] as $memberId => $status) {
$stmt = $db->prepare("
INSERT INTO program_attendance (program_id, member_id, attendance_date, status, marked_by, check_in_time)
VALUES (:program_id, :member_id, :attendance_date, :status, :marked_by, NOW())
ON DUPLICATE KEY UPDATE status = :status, marked_by = :marked_by, check_in_time = NOW()
");
$stmt->execute([
'program_id' => $programId,
'member_id' => $memberId,
'attendance_date' => $selectedDate,
'status' => $status,
'marked_by' => $_SESSION['user_id']
]);
}
$db->commit();
$success = "Attendance marked successfully!";
} catch (Exception $e) {
$db->rollBack();
$error = "Error marking attendance: " . $e->getMessage();
}
}
// Get districts and assemblies for filters
$districts = $db->query("SELECT id, district_name FROM districts WHERE is_active = 1 ORDER BY district_name")->fetchAll();
$assemblies = $db->query("SELECT id, assembly_name, district_id FROM assemblies WHERE is_active = 1 ORDER BY assembly_name")->fetchAll();
// Get members for attendance with filters
$query = "
SELECT m.id, m.first_name, m.last_name, m.membershipcard_id, m.district_id, m.assembly_id,
d.district_name, a.assembly_name,
pa.status as attendance_status, pa.check_in_time
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 program_attendance pa ON m.id = pa.member_id
AND pa.program_id = :program_id
AND pa.attendance_date = :date
WHERE m.is_active = 1";
$params = ['program_id' => $programId, 'date' => $selectedDate];
if ($filterDistrict) {
$query .= " AND m.district_id = :district_id";
$params['district_id'] = $filterDistrict;
}
if ($filterAssembly) {
$query .= " AND m.assembly_id = :assembly_id";
$params['assembly_id'] = $filterAssembly;
}
$query .= " ORDER BY m.first_name, m.last_name";
$stmt = $db->prepare($query);
$stmt->execute($params);
$members = $stmt->fetchAll();
// Get attendance statistics for this program
$statsStmt = $db->prepare("
SELECT
COUNT(DISTINCT member_id) as total_attended,
SUM(CASE WHEN status = 'present' THEN 1 ELSE 0 END) as present_count,
SUM(CASE WHEN status = 'absent' THEN 1 ELSE 0 END) as absent_count,
SUM(CASE WHEN status = 'late' THEN 1 ELSE 0 END) as late_count
FROM program_attendance
WHERE program_id = :program_id AND attendance_date = :date
");
$statsStmt->execute(['program_id' => $programId, 'date' => $selectedDate]);
$stats = $statsStmt->fetch();
include '../../includes/header.php';
?>
<?php include '../../includes/sidebar.php'; ?>
<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">
<div class="mb-6 flex justify-between items-center">
<div>
<h1 class="text-3xl font-bold text-gray-800">
<i class="fas fa-clipboard-check mr-2 text-blue-500"></i>Program Attendance
</h1>
<p class="text-gray-600 mt-2"><?php echo htmlspecialchars($program['program_name']); ?></p>
</div>
<a href="index.php" class="bg-gray-500 text-white px-4 py-2 rounded-lg hover:bg-gray-600 transition">
<i class="fas fa-arrow-left mr-2"></i>Back to Programs
</a>
</div>
<?php if ($success): ?>
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">
<i class="fas fa-check-circle mr-2"></i><?php echo $success; ?>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
<i class="fas fa-exclamation-circle mr-2"></i><?php echo $error; ?>
</div>
<?php endif; ?>
<!-- Date Selector and Filters -->
<div class="bg-white rounded-lg shadow p-6 mb-6">
<h3 class="text-lg font-semibold text-gray-800 mb-4">
<i class="fas fa-filter mr-2"></i>Filters
</h3>
<form method="GET" class="grid grid-cols-1 md:grid-cols-4 gap-4">
<input type="hidden" name="program_id" value="<?php echo $programId; ?>">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Select Date</label>
<input type="date" name="date" value="<?php echo $selectedDate; ?>"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Filter by District</label>
<select name="filter_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 $district): ?>
<option value="<?php echo $district['id']; ?>" <?php echo $filterDistrict == $district['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($district['district_name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Filter by Assembly</label>
<select name="filter_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 $assembly): ?>
<option value="<?php echo $assembly['id']; ?>"
data-district-id="<?php echo $assembly['district_id']; ?>"
<?php echo $filterAssembly == $assembly['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($assembly['assembly_name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="flex gap-2 items-end">
<button type="submit" class="bg-blue-500 text-white px-6 py-2 rounded-lg hover:bg-blue-600 transition flex-1">
<i class="fas fa-filter mr-2"></i>Apply
</button>
<a href="?program_id=<?php echo $programId; ?>"
class="bg-gray-500 text-white px-4 py-2 rounded-lg hover:bg-gray-600 transition">
<i class="fas fa-times"></i>
</a>
</div>
</form>
</div>
<!-- Statistics -->
<div class="grid grid-cols-1 md:grid-cols-4 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 count($members); ?></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">Present</p>
<p class="text-2xl font-bold text-gray-800"><?php echo $stats['present_count'] ?? 0; ?></p>
</div>
<i class="fas fa-check-circle text-3xl text-green-500"></i>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4 border-l-4 border-red-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">Absent</p>
<p class="text-2xl font-bold text-gray-800"><?php echo $stats['absent_count'] ?? 0; ?></p>
</div>
<i class="fas fa-times-circle text-3xl text-red-500"></i>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4 border-l-4 border-yellow-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">Late</p>
<p class="text-2xl font-bold text-gray-800"><?php echo $stats['late_count'] ?? 0; ?></p>
</div>
<i class="fas fa-clock text-3xl text-yellow-500"></i>
</div>
</div>
</div>
<!-- Attendance Form -->
<div class="bg-white rounded-lg shadow">
<div class="p-6">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Mark Attendance for <?php echo date('F j, Y', strtotime($selectedDate)); ?></h3>
<form method="POST">
<div class="mb-4 flex justify-between items-center">
<div class="flex gap-2">
<button type="button" onclick="markAll('present')"
class="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 text-sm">
<i class="fas fa-check mr-2"></i>Mark All Present
</button>
<button type="button" onclick="markAll('absent')"
class="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600 text-sm">
<i class="fas fa-times mr-2"></i>Mark All Absent
</button>
</div>
<button type="submit" name="mark_attendance"
class="bg-blue-500 text-white px-6 py-2 rounded-lg hover:bg-blue-600">
<i class="fas fa-save mr-2"></i>Save Attendance
</button>
</div>
<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">#</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Member</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Member ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">District</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Assembly</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Check-in Time</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<?php $count = 1; foreach ($members as $member): ?>
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<?php echo $count++; ?>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">
<?php echo htmlspecialchars($member['first_name'] . ' ' . $member['last_name']); ?>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<?php echo htmlspecialchars($member['membershipcard_id']); ?>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
<?php echo htmlspecialchars($member['district_name'] ?? 'N/A'); ?>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
<?php echo htmlspecialchars($member['assembly_name'] ?? 'N/A'); ?>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<select name="attendance[<?php echo $member['id']; ?>]"
class="attendance-select px-3 py-1 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500">
<option value="present" <?php echo $member['attendance_status'] === 'present' ? 'selected' : ''; ?>>Present</option>
<option value="absent" <?php echo $member['attendance_status'] === 'absent' ? 'selected' : ''; ?>>Absent</option>
<option value="late" <?php echo $member['attendance_status'] === 'late' ? 'selected' : ''; ?>>Late</option>
<option value="excused" <?php echo $member['attendance_status'] === 'excused' ? 'selected' : ''; ?>>Excused</option>
</select>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<?php echo $member['check_in_time'] ? date('g:i A', strtotime($member['check_in_time'])) : '-'; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</form>
</div>
</div>
</div>
</div>
</main>
<script>
function markAll(status) {
const selects = document.querySelectorAll('.attendance-select');
selects.forEach(select => {
select.value = status;
});
}
// Cascading filter: Assembly based on District
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 = ''; // Always show "All Assemblies"
return;
}
const districtId = option.getAttribute('data-district-id');
if (selectedDistrict === '' || districtId === selectedDistrict) {
option.style.display = '';
} else {
option.style.display = 'none';
}
});
// Reset assembly selection if current selection is hidden
const currentAssembly = filterAssembly.value;
if (currentAssembly) {
const currentOption = filterAssembly.querySelector(`option[value="${currentAssembly}"]`);
if (currentOption && currentOption.style.display === 'none') {
filterAssembly.value = ''; // Reset to "All Assemblies"
}
}
});
}
</script>
<?php include '../../includes/footer.php'; ?>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists