BROOKO icon
BROOKO UK NETWORK
Where code meets creativity & adventure
File viewer

clock.php

Type
php
Size
29.23 KB
Modified
15 May
clock.php 29.23 KB
<?php
require_once __DIR__ . '/../bootstrap.php';
require_once __DIR__ . '/../lib/feature_modules.php';
requireAuth();

global $pdo;
$pageTitle = 'Shift Clock';
$currentUser = getCurrentUser();
$userId = (int)($_SESSION['user_id'] ?? 0);
$wpRole = (string)($currentUser['role'] ?? ($_SESSION['user_role'] ?? ''));

wp_feature_ensure_time_clock_schema();

$roleMode = wp_feature_clock_role_mode($wpRole);
$startConfig = wp_feature_clock_start_requirements($wpRole);
$checkoutConfig = wp_feature_clock_checkout_requirements($wpRole);
$clockStatus = wp_clock_status($userId);
$message = null;
$messageType = 'success';

function wp_clock_post_text(string $key): string {
    return trim((string)($_POST[$key] ?? ''));
}

function wp_clock_uploaded_count(?array $files): int {
    if (!is_array($files) || !isset($files['name']) || !is_array($files['name'])) return 0;
    $count = 0;
    foreach ($files['name'] as $name) {
        if (trim((string)$name) !== '') $count++;
    }
    return $count;
}

function wp_clock_collect_checks(array $config, string $fieldName = 'check'): array {
    $checks = [];
    foreach (($config['items'] ?? []) as $key => $label) {
        $checks[$key] = !empty($_POST[$fieldName][$key]);
    }
    return $checks;
}

function wp_clock_missing_checks(array $config, array $checks): array {
    $errors = [];
    foreach (($config['items'] ?? []) as $key => $label) {
        if (empty($checks[$key])) {
            $errors[] = $label . ' must be ticked.';
        }
    }
    return $errors;
}

function wp_clock_int_or_null(string $value): ?int {
    $value = trim($value);
    if ($value === '') return null;
    if (!preg_match('/^\d+$/', $value)) return null;
    return (int)$value;
}

function wp_clock_config_label(array $config, string $key, string $fallback): string {
    return (string)($config[$key] ?? $fallback);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($pdo) && $pdo instanceof PDO && wp_db_table_exists('time_clock_entries')) {
    $action = (string)($_POST['action'] ?? '');

    if ($action === 'clock_in' && ($clockStatus['status'] ?? '') === 'not_clocked_in') {
        $formErrors = [];
        $startChecks = wp_clock_collect_checks($startConfig, 'start_check');
        $formErrors = array_merge($formErrors, wp_clock_missing_checks($startConfig, $startChecks));

        $vanLabel = wp_clock_post_text('van_label');
        $vanReg = wp_clock_post_text('van_reg');
        $partnerName = wp_clock_post_text('partner_name');
        $mileageInRaw = wp_clock_post_text('mileage_in');
        $mileageIn = wp_clock_int_or_null($mileageInRaw);
        $startNotes = wp_clock_post_text('start_note');
        $startPhotos = $_FILES['start_photos'] ?? null;
        $startPhotoRequested = wp_clock_uploaded_count(is_array($startPhotos) ? $startPhotos : null);
        $startPhotoMin = (int)($startConfig['photo_min'] ?? 0);

        if (!empty($startConfig['require_van']) && $vanLabel === '') {
            $formErrors[] = 'Van number / assignment is required.';
        }
        if (!empty($startConfig['require_partner']) && $partnerName === '') {
            $formErrors[] = wp_clock_config_label($startConfig, 'partner_label', 'Working with') . ' is required.';
        }
        if (!empty($startConfig['require_van_reg']) && $vanReg === '') {
            $formErrors[] = 'Van registration is required.';
        }
        if (!empty($startConfig['require_mileage']) && $mileageIn === null) {
            $formErrors[] = 'Start odometer reading is required.';
        }
        if ($startPhotoRequested < $startPhotoMin) {
            $formErrors[] = 'Drivers must upload at least ' . $startPhotoMin . ' start-of-shift photos.';
        }

        $perm = (string)($startConfig['permission'] ?? '');
        if ($perm !== '' && !hasPermission($perm) && !isAdminRole()) {
            $formErrors[] = 'Your account is missing the permission needed to start this shift type.';
        }
        $photoPerm = (string)($startConfig['photo_permission'] ?? '');
        if ($startPhotoRequested > 0 && $photoPerm !== '' && !hasPermission($photoPerm) && !isAdminRole()) {
            $formErrors[] = 'Your account is missing permission to upload shift photos.';
        }

        if ($formErrors) {
            $message = implode(' ', array_values(array_unique($formErrors)));
            $messageType = 'error';
        } else {
            try {
                $startMeta = [
                    'mode' => $roleMode,
                    'checks' => $startChecks,
                    'note' => $startNotes,
                    'van_label' => $vanLabel,
                    'van_reg' => $vanReg,
                    'partner_name' => $partnerName,
                    'mileage_in' => $mileageIn,
                    'photos_expected' => $startPhotoRequested,
                    'source' => 'database_shift_clock',
                ];
                $stmt = $pdo->prepare("INSERT INTO time_clock_entries (user_id, username, display_name, role_name, van_label, van_reg, partner_name, mileage_in, clock_in_at, status, ip_address, notes, start_meta_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), 'clocked_in', ?, ?, ?)");
                $stmt->execute([
                    $userId,
                    (string)($currentUser['username'] ?? ''),
                    (string)($currentUser['display_name'] ?? ''),
                    $wpRole,
                    $vanLabel !== '' ? $vanLabel : null,
                    $vanReg !== '' ? $vanReg : null,
                    $partnerName !== '' ? $partnerName : null,
                    $mileageIn,
                    $_SERVER['REMOTE_ADDR'] ?? null,
                    $startNotes !== '' ? $startNotes : null,
                    json_encode($startMeta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
                ]);
                $entryId = (int)$pdo->lastInsertId();

                $uploadErrors = [];
                $savedPhotos = 0;
                if ($startPhotoRequested > 0 && is_array($startPhotos)) {
                    $uploadResult = wp_feature_clock_upload_files($entryId, $startPhotos, 'clock_in');
                    $savedPhotos = (int)($uploadResult['saved'] ?? 0);
                    $uploadErrors = $uploadResult['errors'] ?? [];
                    if ($savedPhotos < $startPhotoMin) {
                        $uploadErrors[] = 'The required start-of-shift photos could not be saved.';
                    }
                }

                if ($uploadErrors) {
                    if (wp_db_table_exists('time_clock_photos')) { $pdo->prepare("DELETE FROM time_clock_photos WHERE clock_entry_id = ?")->execute([$entryId]); }
                    $pdo->prepare("DELETE FROM time_clock_entries WHERE id = ? AND user_id = ?")->execute([$entryId, $userId]);
                    $message = implode(' ', array_values(array_unique($uploadErrors)));
                    $messageType = 'error';
                } else {
                    logActivity('clock.in', 'time_clock_entries', $entryId, 'Started shift');
                    $message = 'Shift started successfully.';
                }
                $clockStatus = wp_clock_status($userId);
            } catch (Throwable $e) {
                $message = 'Clock in failed. Please try again.';
                $messageType = 'error';
            }
        }
    }

    if ($action === 'clock_out' && ($clockStatus['status'] ?? '') === 'clocked_in' && !empty($clockStatus['entry']['id'])) {
        $entryId = (int)$clockStatus['entry']['id'];
        $checkoutChecks = wp_clock_collect_checks($checkoutConfig, 'check');
        $formErrors = wp_clock_missing_checks($checkoutConfig, $checkoutChecks);

        $existingEntry = $clockStatus['entry'] ?? [];
        $vanLabel = wp_clock_post_text('van_label');
        $vanReg = wp_clock_post_text('van_reg');
        $partnerName = wp_clock_post_text('partner_name');
        if ($vanLabel === '' && !empty($existingEntry['van_label'])) $vanLabel = (string)$existingEntry['van_label'];
        if ($vanReg === '' && !empty($existingEntry['van_reg'])) $vanReg = (string)$existingEntry['van_reg'];
        if ($partnerName === '' && !empty($existingEntry['partner_name'])) $partnerName = (string)$existingEntry['partner_name'];

        $mileageOutRaw = wp_clock_post_text('mileage_out');
        $mileageOut = wp_clock_int_or_null($mileageOutRaw);
        $endNotes = wp_clock_post_text('end_note');
        $endPhotos = $_FILES['end_photos'] ?? null;
        $endPhotoRequested = wp_clock_uploaded_count(is_array($endPhotos) ? $endPhotos : null);
        $endPhotoMin = (int)($checkoutConfig['photo_min'] ?? 0);

        if (!empty($checkoutConfig['require_van']) && $vanLabel === '') {
            $formErrors[] = 'Van number / assignment is required before ending this shift.';
        }
        if (!empty($checkoutConfig['require_partner']) && $partnerName === '') {
            $formErrors[] = wp_clock_config_label($checkoutConfig, 'partner_label', 'Working with') . ' is required before ending this shift.';
        }
        if (!empty($checkoutConfig['require_mileage']) && $mileageOut === null) {
            $formErrors[] = 'End odometer reading is required.';
        }
        if ($endPhotoRequested < $endPhotoMin) {
            $formErrors[] = 'Drivers must upload at least ' . $endPhotoMin . ' end-of-shift photos.';
        }

        $perm = (string)($checkoutConfig['permission'] ?? '');
        if ($perm !== '' && !hasPermission($perm) && !isAdminRole()) {
            $formErrors[] = 'Your account is missing the permission needed to complete this shift check.';
        }
        $photoPerm = (string)($checkoutConfig['photo_permission'] ?? '');
        if ($endPhotoRequested > 0 && $photoPerm !== '' && !hasPermission($photoPerm) && !isAdminRole()) {
            $formErrors[] = 'Your account is missing permission to upload shift photos.';
        }

        if ($formErrors) {
            $message = implode(' ', array_values(array_unique($formErrors)));
            $messageType = 'error';
        } else {
            try {
                $endMeta = [
                    'mode' => $roleMode,
                    'checks' => $checkoutChecks,
                    'note' => $endNotes,
                    'van_label' => $vanLabel,
                    'van_reg' => $vanReg,
                    'partner_name' => $partnerName,
                    'mileage_out' => $mileageOut,
                    'photos_expected' => $endPhotoRequested,
                    'source' => 'database_shift_clock',
                ];
                $stmt = $pdo->prepare("UPDATE time_clock_entries SET van_label = COALESCE(NULLIF(?, ''), van_label), van_reg = COALESCE(NULLIF(?, ''), van_reg), partner_name = COALESCE(NULLIF(?, ''), partner_name), mileage_out = ?, clock_out_at = NOW(), status = 'clocked_out', notes = ?, end_meta_json = ? WHERE id = ? AND user_id = ?");
                $stmt->execute([
                    $vanLabel,
                    $vanReg,
                    $partnerName,
                    $mileageOut,
                    $endNotes !== '' ? $endNotes : null,
                    json_encode($endMeta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
                    $entryId,
                    $userId,
                ]);

                $uploadErrors = [];
                $savedPhotos = 0;
                if ($endPhotoRequested > 0 && is_array($endPhotos)) {
                    $uploadResult = wp_feature_clock_upload_files($entryId, $endPhotos, 'clock_out');
                    $savedPhotos = (int)($uploadResult['saved'] ?? 0);
                    $uploadErrors = $uploadResult['errors'] ?? [];
                    if ($savedPhotos < $endPhotoMin) {
                        $uploadErrors[] = 'The required end-of-shift photos could not be saved.';
                    }
                }
                if ($uploadErrors) {
                    $message = implode(' ', array_values(array_unique($uploadErrors)));
                    $messageType = 'error';
                    if ($endPhotoMin > 0) {
                        $undo = $pdo->prepare("UPDATE time_clock_entries SET clock_out_at = NULL, status = 'clocked_in' WHERE id = ? AND user_id = ?");
                        $undo->execute([$entryId, $userId]);
                    }
                } else {
                    logActivity('clock.out', 'time_clock_entries', $entryId, 'Ended shift');
                    $message = 'Shift ended successfully.';
                }
                $clockStatus = wp_clock_status($userId);
            } catch (Throwable $e) {
                $message = 'Clock out failed. Please try again.';
                $messageType = 'error';
            }
        }
    }
}

$rotaToday = null;
try {
    $rotaRows = function_exists('wp_feature_fetch_rota_for_current_user') ? wp_feature_fetch_rota_for_current_user(date('Y-m-d'), 2) : [];
    $rotaToday = $rotaRows[0] ?? null;
} catch (Throwable $e) {
    $rotaToday = null;
}

$clockRequired = wp_clock_required_for_current_user();
$clockLabel = 'Not clocked in yet';
$clockMeta = $clockRequired ? 'You need to start your shift before you can use the app.' : 'Shift clocking is available for your role, but it is optional.';
if (($clockStatus['status'] ?? '') === 'clocked_in') {
    $clockLabel = 'Currently on shift';
    $clockMeta = 'Shift started at ' . date('H:i', strtotime((string)$clockStatus['entry']['clock_in_at']));
} elseif (($clockStatus['status'] ?? '') === 'shift_ended') {
    $clockLabel = 'Shift completed';
    $in = !empty($clockStatus['entry']['clock_in_at']) ? date('H:i', strtotime((string)$clockStatus['entry']['clock_in_at'])) : '—';
    $out = !empty($clockStatus['entry']['clock_out_at']) ? date('H:i', strtotime((string)$clockStatus['entry']['clock_out_at'])) : '—';
    $hours = (!empty($clockStatus['entry']['clock_in_at']) && !empty($clockStatus['entry']['clock_out_at'])) ? number_format(max(0, (strtotime((string)$clockStatus['entry']['clock_out_at']) - strtotime((string)$clockStatus['entry']['clock_in_at']))) / 3600, 2) : '0.00';
    $clockMeta = 'Today: ' . $in . ' to ' . $out . ' (' . $hours . 'h)';
}

$entry = $clockStatus['entry'] ?? [];
$defaultVan = (string)($entry['van_label'] ?? ($rotaToday['van_label'] ?? ''));
$defaultReg = (string)($entry['van_reg'] ?? '');
$defaultPartner = (string)($entry['partner_name'] ?? '');
$modeName = $roleMode === 'driver' ? 'Driver' : ($roleMode === 'porter' ? 'Porter' : 'General');

$extraHead = '<style>
.shift-clock-page{--clock-gap:18px;}
.shift-clock-grid{display:grid;grid-template-columns:minmax(0,1.12fr) minmax(320px,.88fr);gap:var(--clock-gap);align-items:start;}
.shift-clock-hero,.shift-clock-panel{border:1px solid var(--border);border-radius:22px;background:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,.018));box-shadow:0 18px 45px rgba(0,0,0,.18);}
.shift-clock-hero{padding:22px;}
.shift-clock-panel{padding:18px;}
.shift-clock-status{display:inline-flex;align-items:center;gap:10px;padding:9px 13px;border-radius:999px;background:rgba(255,255,255,.05);border:1px solid var(--border);font-weight:850;}
.shift-clock-dot{width:11px;height:11px;border-radius:999px;background:#ef4444;box-shadow:0 0 0 6px rgba(239,68,68,.10);}
.shift-clock-dot.is-on{background:#22c55e;box-shadow:0 0 0 6px rgba(34,197,94,.14);}
.shift-clock-title{font-size:clamp(1.45rem,3vw,2.35rem);line-height:1;margin:16px 0 8px 0;font-weight:900;}
.shift-clock-meta{color:var(--text-muted);font-size:1rem;margin:0;}
.shift-clock-action{margin-top:18px;display:grid;gap:16px;}
.shift-clock-form{display:grid;gap:16px;}
.shift-clock-section{border:1px solid var(--border);border-radius:18px;background:rgba(0,0,0,.12);padding:16px;display:grid;gap:14px;}
.shift-clock-section h3{margin:0;font-size:1.05rem;font-weight:900;}
.shift-clock-fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;}
.shift-clock-field{display:grid;gap:7px;}
.shift-clock-field label,.shift-clock-check strong{font-weight:800;}
.shift-clock-help{font-size:.88rem;color:var(--text-muted);}
.shift-clock-checks{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
.shift-clock-check{display:flex;gap:10px;align-items:flex-start;border:1px solid var(--border);border-radius:14px;background:rgba(255,255,255,.025);padding:11px 12px;}
.shift-clock-check input{width:18px;height:18px;margin-top:2px;accent-color:var(--accent);}
.shift-clock-photos{display:grid;gap:8px;padding:13px;border:1px dashed rgba(255,140,26,.45);border-radius:16px;background:rgba(255,140,26,.055);}
.shift-clock-photos input{padding:10px;background:rgba(0,0,0,.18);border-radius:12px;border:1px solid var(--border);}
.shift-clock-buttons{display:flex;gap:12px;flex-wrap:wrap;align-items:center;}
.shift-clock-list{display:grid;gap:10px;}
.shift-clock-list-item{display:flex;justify-content:space-between;gap:14px;align-items:flex-start;padding:11px 12px;border:1px solid var(--border);border-radius:14px;background:rgba(255,255,255,.025);}
.shift-clock-list-item span{color:var(--text-muted);}
.shift-clock-list-item strong{text-align:right;}
.shift-clock-pill{display:inline-flex;align-items:center;gap:8px;border-radius:999px;padding:8px 11px;background:rgba(255,140,26,.10);color:var(--accent);border:1px solid rgba(255,140,26,.25);font-weight:800;}
.shift-clock-note{min-height:108px;}
.shift-clock-alert{margin-bottom:16px;}
@media (max-width:1050px){.shift-clock-grid{grid-template-columns:1fr;}.shift-clock-panel{order:2;}}
@media (max-width:720px){.shift-clock-hero,.shift-clock-panel{border-radius:18px;padding:15px;}.shift-clock-fields,.shift-clock-checks{grid-template-columns:1fr;}.shift-clock-buttons .btn{width:100%;justify-content:center;}.shift-clock-list-item{display:grid;grid-template-columns:1fr;}.shift-clock-list-item strong{text-align:left;}.content-header{display:block;}}
</style>';

include __DIR__ . '/../partials/header.php';

function wp_clock_render_checks(array $config, string $namePrefix): void {
    if (empty($config['items']) || !is_array($config['items'])) return;
    echo '<div class="shift-clock-section"><h3>' . e((string)$config['title']) . '</h3><div class="shift-clock-checks">';
    foreach ($config['items'] as $key => $label) {
        echo '<label class="shift-clock-check"><input type="checkbox" name="' . e($namePrefix) . '[' . e((string)$key) . ']" value="1"><span><strong>' . e((string)$label) . '</strong><br><small class="shift-clock-help">Required before continuing.</small></span></label>';
    }
    echo '</div></div>';
}
?>
<div class="shift-clock-page">
    <div class="content-header">
        <div>
            <h1 class="content-title">⏱️ Shift Clock</h1>
            <p class="content-subtitle"><?= e($clockRequired ? 'Start your shift to unlock the rest of the app.' : 'Start or end your shift here. Admin and Director accounts can still use the app without clocking in.') ?></p>
        </div>
        <span class="shift-clock-pill"><?= e($modeName) ?> workflow</span>
    </div>

    <?php if ($message): ?>
        <div class="alert alert-<?= $messageType === 'error' ? 'error' : 'success' ?> shift-clock-alert"><?= e($message) ?></div>
    <?php endif; ?>

    <div class="shift-clock-grid">
        <section class="shift-clock-hero">
            <div class="shift-clock-status">
                <span class="shift-clock-dot <?= ($clockStatus['status'] ?? '') === 'clocked_in' ? 'is-on' : '' ?>"></span>
                <span><?= e($clockLabel) ?></span>
            </div>
            <h2 class="shift-clock-title"><?= e((string)($currentUser['display_name'] ?? 'User')) ?></h2>
            <p class="shift-clock-meta"><?= e(ucwords(str_replace('-', ' ', (string)($currentUser['role'] ?? 'staff')))) ?> · <?= e($clockMeta) ?></p>

            <div class="shift-clock-action">
                <?php if (($clockStatus['status'] ?? '') === 'not_clocked_in'): ?>
                    <form method="POST" enctype="multipart/form-data" class="shift-clock-form">
                        <input type="hidden" name="action" value="clock_in">
                        <div class="shift-clock-section">
                            <h3><?= e((string)$startConfig['title']) ?></h3>
                            <div class="shift-clock-fields">
                                <?php if (!empty($startConfig['require_van'])): ?>
                                    <div class="shift-clock-field">
                                        <label for="van_label">Van / assignment <?= !empty($startConfig['require_van']) ? '*' : '' ?></label>
                                        <input class="form-control" id="van_label" name="van_label" value="<?= e($defaultVan) ?>" placeholder="Van number or route">
                                    </div>
                                <?php endif; ?>
                                <?php if (!empty($startConfig['require_van_reg'])): ?>
                                    <div class="shift-clock-field">
                                        <label for="van_reg">Van registration *</label>
                                        <input class="form-control" id="van_reg" name="van_reg" value="<?= e($defaultReg) ?>" placeholder="Registration">
                                    </div>
                                <?php endif; ?>
                                <?php if (!empty($startConfig['require_partner'])): ?>
                                    <div class="shift-clock-field">
                                        <label for="partner_name"><?= e(wp_clock_config_label($startConfig, 'partner_label', 'Working with')) ?> *</label>
                                        <input class="form-control" id="partner_name" name="partner_name" value="<?= e($defaultPartner) ?>" placeholder="Name">
                                    </div>
                                <?php endif; ?>
                                <?php if (!empty($startConfig['require_mileage'])): ?>
                                    <div class="shift-clock-field">
                                        <label for="mileage_in">Start odometer *</label>
                                        <input class="form-control" id="mileage_in" name="mileage_in" type="number" inputmode="numeric" min="0" placeholder="e.g. 45823">
                                    </div>
                                <?php endif; ?>
                            </div>
                        </div>

                        <?php wp_clock_render_checks($startConfig, 'start_check'); ?>

                        <?php if ((int)($startConfig['photo_min'] ?? 0) > 0): ?>
                            <div class="shift-clock-photos">
                                <label class="form-label" for="start_photos">Start-of-shift photos *</label>
                                <input id="start_photos" type="file" name="start_photos[]" accept="image/*" multiple>
                                <div class="shift-clock-help">Drivers must upload at least <?= (int)$startConfig['photo_min'] ?> start photos. These are saved to OneDrive when configured.</div>
                            </div>
                        <?php endif; ?>

                        <div class="shift-clock-field">
                            <label for="start_note">Start note</label>
                            <textarea class="form-control shift-clock-note" id="start_note" name="start_note" rows="3" placeholder="Anything to record before starting?"></textarea>
                        </div>
                        <div class="shift-clock-buttons">
                            <button type="submit" class="btn btn-primary">Start Shift</button>
                            <a href="<?= e(app_url('logout')) ?>" class="btn btn-secondary">Logout</a>
                        </div>
                    </form>
                <?php elseif (($clockStatus['status'] ?? '') === 'clocked_in'): ?>
                    <form method="POST" enctype="multipart/form-data" class="shift-clock-form">
                        <input type="hidden" name="action" value="clock_out">
                        <div class="shift-clock-section">
                            <h3>End shift details</h3>
                            <div class="shift-clock-fields">
                                <?php if (!empty($checkoutConfig['require_van'])): ?>
                                    <div class="shift-clock-field">
                                        <label for="co_van_label">Van / assignment *</label>
                                        <input class="form-control" id="co_van_label" name="van_label" value="<?= e($defaultVan) ?>" placeholder="Van number or route">
                                    </div>
                                <?php endif; ?>
                                <?php if (!empty($checkoutConfig['require_partner'])): ?>
                                    <div class="shift-clock-field">
                                        <label for="co_partner_name"><?= e(wp_clock_config_label($checkoutConfig, 'partner_label', 'Working with')) ?> *</label>
                                        <input class="form-control" id="co_partner_name" name="partner_name" value="<?= e($defaultPartner) ?>" placeholder="Name">
                                    </div>
                                <?php endif; ?>
                                <?php if (!empty($checkoutConfig['require_mileage'])): ?>
                                    <div class="shift-clock-field">
                                        <label for="mileage_out">End odometer *</label>
                                        <input class="form-control" id="mileage_out" name="mileage_out" type="number" inputmode="numeric" min="0" placeholder="e.g. 45991">
                                    </div>
                                <?php endif; ?>
                            </div>
                        </div>

                        <?php wp_clock_render_checks($checkoutConfig, 'check'); ?>

                        <?php if ((int)($checkoutConfig['photo_min'] ?? 0) > 0): ?>
                            <div class="shift-clock-photos">
                                <label class="form-label" for="end_photos">End-of-shift photos *</label>
                                <input id="end_photos" type="file" name="end_photos[]" accept="image/*" multiple>
                                <div class="shift-clock-help">Drivers must upload at least <?= (int)$checkoutConfig['photo_min'] ?> end photos before ending the shift.</div>
                            </div>
                        <?php endif; ?>

                        <div class="shift-clock-field">
                            <label for="end_note">End note</label>
                            <textarea class="form-control shift-clock-note" id="end_note" name="end_note" placeholder="Anything to record before you end the shift?"></textarea>
                        </div>
                        <div class="shift-clock-buttons">
                            <button type="submit" class="btn btn-secondary">End Shift</button>
                            <a href="<?= e(app_url()) ?>" class="btn btn-outline-light">Continue to App</a>
                        </div>
                    </form>
                <?php else: ?>
                    <div class="shift-clock-buttons">
                        <a href="<?= e(app_url()) ?>" class="btn btn-primary">Continue to App</a>
                        <a href="<?= e(app_url('logout')) ?>" class="btn btn-secondary">Logout</a>
                    </div>
                <?php endif; ?>
            </div>
        </section>

        <aside class="shift-clock-panel">
            <h3 style="margin-top:0;">Today's rota</h3>
            <?php if ($rotaToday): ?>
                <div class="shift-clock-list">
                    <div class="shift-clock-list-item"><span>Route</span><strong><?= e((string)($rotaToday['route_name'] ?: 'Not set')) ?></strong></div>
                    <div class="shift-clock-list-item"><span>Van</span><strong><?= e((string)($rotaToday['van_label'] ?: 'Not set')) ?></strong></div>
                    <div class="shift-clock-list-item"><span>Role</span><strong><?= e((string)($rotaToday['role_label'] ?: 'Not set')) ?></strong></div>
                    <div class="shift-clock-list-item"><span>Notes</span><strong><?= e((string)($rotaToday['notes'] ?: '—')) ?></strong></div>
                </div>
            <?php else: ?>
                <p class="text-muted" style="margin:0 0 12px 0;">No rota entry has been assigned for you today.</p>
            <?php endif; ?>

            <hr>
            <h3>Role requirements</h3>
            <div class="shift-clock-list">
                <?php if ($roleMode === 'driver'): ?>
                    <div class="shift-clock-list-item"><span>Start</span><strong>Van, porter/helper, reg, odometer and <?= (int)$startConfig['photo_min'] ?> photos</strong></div>
                    <div class="shift-clock-list-item"><span>End</span><strong>Checks, odometer and <?= (int)$checkoutConfig['photo_min'] ?> photos</strong></div>
                <?php elseif ($roleMode === 'porter'): ?>
                    <div class="shift-clock-list-item"><span>Start</span><strong>Assigned van/team and PPE confirmation</strong></div>
                    <div class="shift-clock-list-item"><span>End</span><strong>Clear-up, equipment and issue-report confirmation</strong></div>
                <?php else: ?>
                    <div class="shift-clock-list-item"><span>Start / End</span><strong>Simple shift clock with notes</strong></div>
                <?php endif; ?>
                <div class="shift-clock-list-item"><span>Review</span><strong>Managers and admins can view daily records</strong></div>
            </div>
        </aside>
    </div>
</div>

<?php include __DIR__ . '/../partials/footer.php'; ?>