132 lines
6.2 KiB
PHP
132 lines
6.2 KiB
PHP
<?php
|
||
require __DIR__ . '/../includes/bootstrap.php';
|
||
require_login();
|
||
require_admin();
|
||
|
||
$pdo = db();
|
||
|
||
$activities = $pdo->query(
|
||
'SELECT id, name, date, time_in, time_out
|
||
FROM activities
|
||
WHERE status = 1 AND date >= CURDATE() - INTERVAL 1 DAY
|
||
ORDER BY date DESC, time_in DESC'
|
||
)->fetchAll();
|
||
$hasActivities = !empty($activities);
|
||
|
||
$todayAttendance = $pdo->query(
|
||
'SELECT att.*, s.full_name, s.student_id, act.name AS activity_name
|
||
FROM attendance att
|
||
INNER JOIN students s ON att.student_id = s.id
|
||
INNER JOIN activities act ON att.activity_id = act.id
|
||
WHERE DATE(att.time_in) = CURDATE()
|
||
ORDER BY att.time_in DESC'
|
||
)->fetchAll();
|
||
|
||
render_header('Scan Center', ['active' => 'attendance']);
|
||
?>
|
||
<div class="row g-4">
|
||
<div class="col-lg-5">
|
||
<div class="scan-panel h-100">
|
||
<h2>Admin Scan Center</h2>
|
||
<p class="mb-4 text-white-50">Only signed-in administrators can scan QR codes. Use the camera or type the token below.</p>
|
||
<?php if (!$hasActivities): ?>
|
||
<div class="alert alert-warning bg-warning-subtle text-dark">
|
||
No active activities available. Create one first to start scanning.
|
||
</div>
|
||
<?php endif; ?>
|
||
<form data-scan-form data-endpoint="<?= url_for('api/scan.php') ?>" class="v-stack gap-3">
|
||
<div>
|
||
<label class="form-label text-white">Activity</label>
|
||
<select name="activity_id" class="form-select form-select-lg" <?= $hasActivities ? '' : 'disabled' ?> required>
|
||
<?php if ($hasActivities): ?>
|
||
<?php foreach ($activities as $activity): ?>
|
||
<option value="<?= $activity['id'] ?>">
|
||
<?= sanitize($activity['name']) ?> — <?= format_date($activity['date'], 'M d') ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
<?php else: ?>
|
||
<option value="">No activities</option>
|
||
<?php endif; ?>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="form-label text-white">QR Token / Student ID</label>
|
||
<input type="text" name="qr_token" class="form-control form-control-lg"
|
||
placeholder="Scan or type token" autocomplete="off" <?= $hasActivities ? '' : 'disabled' ?> required>
|
||
</div>
|
||
<div>
|
||
<label class="form-label text-white">Notes (optional)</label>
|
||
<textarea name="notes" class="form-control" rows="2" placeholder="Remarks for this scan"></textarea>
|
||
</div>
|
||
<div class="d-flex gap-2 flex-wrap">
|
||
<button type="submit" class="btn btn-light btn-lg flex-grow-1" <?= $hasActivities ? '' : 'disabled' ?>>Record Attendance</button>
|
||
<button type="button" class="btn btn-outline-light btn-lg flex-grow-1" data-scan-start <?= $hasActivities ? '' : 'disabled' ?>>
|
||
Start Camera Scan
|
||
</button>
|
||
</div>
|
||
<p class="text-white-50 small mb-0">
|
||
The scanner auto-selects the best camera mode and falls back to a built-in detector if needed.
|
||
</p>
|
||
</form>
|
||
<div class="mt-4">
|
||
<p id="scan-status" class="fw-semibold mb-3"></p>
|
||
<div id="camera-viewer" class="camera-viewer bg-white rounded shadow-sm">
|
||
<div class="text-center text-muted small">Camera idle</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-7">
|
||
<div class="card shadow-sm h-100">
|
||
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
||
<h5 class="mb-0">Today’s Attendance</h5>
|
||
<span class="badge bg-primary-subtle text-primary"><?= count($todayAttendance) ?> captured</span>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="scan-result" class="mb-4"></div>
|
||
<?php if (!$todayAttendance): ?>
|
||
<p class="text-muted text-center mb-0">No scans recorded today.</p>
|
||
<?php else: ?>
|
||
<div class="table-responsive">
|
||
<table class="table align-middle">
|
||
<thead>
|
||
<tr>
|
||
<th>Student</th>
|
||
<th>Activity</th>
|
||
<th>Time In</th>
|
||
<th>Time Out</th>
|
||
<th>Status</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($todayAttendance as $row): ?>
|
||
<tr>
|
||
<td>
|
||
<strong><?= sanitize($row['full_name']) ?></strong><br>
|
||
<span class="text-muted-sm"><?= sanitize($row['student_id']) ?></span>
|
||
</td>
|
||
<td><?= sanitize($row['activity_name']) ?></td>
|
||
<td><?= format_datetime($row['time_in'], 'h:i A') ?></td>
|
||
<td><?= format_datetime($row['time_out'], 'h:i A') ?></td>
|
||
<td>
|
||
<span class="badge bg-success-subtle text-success badge-status">
|
||
<?= strtoupper(sanitize($row['status'])) ?>
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
render_footer([
|
||
'extra_js' => [
|
||
'https://unpkg.com/html5-qrcode@2.3.10/html5-qrcode.min.js',
|
||
],
|
||
]);
|