<?php
if (!defined('LUNA_APP')) {
    http_response_code(403);
    exit('Forbidden');
}

require_once __DIR__ . '/config/messages.php';
$messages = $messages ?? load_messages();
require_once __DIR__ . '/config/database.php';

// Simple flash helper
function set_flash(string $type, string $msg): void {
    if (session_status() === PHP_SESSION_NONE) session_start();
    $_SESSION['flash'][$type] = $msg;
}

if (session_status() === PHP_SESSION_NONE) { session_start(); }
$logs = $_SESSION['attendance_logs'] ?? [];
$error = null;
$__att_fetch_done = false;
$__att_success_count = 0;
$__att_device = '';
$__att_device_ipport = '';
$__att_created = false;
$__att_created_msg = '';


// Chuẩn hoá mã nhân viên: tối thiểu 5 chữ số, thêm số 0 ở đầu
function format_pin($pin) {
    $s = preg_replace('/\D/', '', (string)$pin);
    if ($s === '') return '';
    if (strlen($s) < 5) return str_pad($s, 5, '0', STR_PAD_LEFT);
    return $s; // nếu dài hơn 5 giữ nguyên
}

// Lấy tên nhân viên theo danh sách mã (tối thiểu 5 chữ số)
function fetch_employee_names(array $pins): array {
    $pins = array_values(array_unique(array_filter(array_map('format_pin', $pins))));
    if (empty($pins)) return [];
    $conn = null; $map = [];
    try {
        $conn = db_connect();
        $ph = implode(',', array_fill(0, count($pins), '?'));
        // Cách 1: so khớp theo chuẩn hóa 5 chữ số (bao phủ trường hợp Maso_NV dạng số hoặc chuỗi có/không 0 đầu)
        $sql1 = "SELECT Maso_NV, Hoten FROM Datawork WHERE RIGHT(REPLICATE('0',5)+RTRIM(CAST(Maso_NV AS NVARCHAR(50))),5) IN ($ph)";
        $stmt = sqlsrv_query($conn, $sql1, $pins, ['Scrollable' => SQLSRV_CURSOR_KEYSET]);
        if ($stmt !== false) {
            while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
                $k = format_pin($row['Maso_NV'] ?? '');
                if ($k !== '') {
                    $map[$k] = (string)($row['Hoten'] ?? '');
                }
            }
            sqlsrv_free_stmt($stmt);
        }
        // Nếu vẫn chưa có kết quả, thử khớp trực tiếp theo chuỗi gốc (trường hợp DB lưu đúng 00092 dạng text)
        if (!$map) {
            $sql2 = "SELECT Maso_NV, Hoten FROM Datawork WHERE CAST(RTRIM(CAST(Maso_NV AS NVARCHAR(50))) AS NVARCHAR(50)) IN ($ph)";
            $stmt2 = sqlsrv_query($conn, $sql2, $pins, ['Scrollable' => SQLSRV_CURSOR_KEYSET]);
            if ($stmt2 !== false) {
                while ($row = sqlsrv_fetch_array($stmt2, SQLSRV_FETCH_ASSOC)) {
                    $k = format_pin($row['Maso_NV'] ?? '');
                    if ($k !== '') {
                        $map[$k] = (string)($row['Hoten'] ?? '');
                    }
                }
                sqlsrv_free_stmt($stmt2);
            }
        }
        // Nếu vẫn rỗng, thử khớp theo số nguyên (DB lưu 92 còn máy trả 00092)
        if (!$map) {
            $pinsInt = array_map('intval', $pins);
            $phi = implode(',', array_fill(0, count($pinsInt), '?'));
            $sql3 = "SELECT Maso_NV, Hoten FROM Datawork WHERE TRY_CONVERT(INT, RTRIM(CAST(Maso_NV AS NVARCHAR(50)))) IN ($phi)";
            $stmt3 = sqlsrv_query($conn, $sql3, $pinsInt, ['Scrollable' => SQLSRV_CURSOR_KEYSET]);
            if ($stmt3 !== false) {
                while ($row = sqlsrv_fetch_array($stmt3, SQLSRV_FETCH_ASSOC)) {
                    $k = format_pin($row['Maso_NV'] ?? '');
                    if ($k !== '') {
                        $map[$k] = (string)($row['Hoten'] ?? '');
                    }
                }
                sqlsrv_free_stmt($stmt3);
            }
        }
    } catch (Throwable $e) {
        // Bỏ qua; không chặn luồng hiển thị nếu DB lỗi
    } finally {
        if ($conn) { db_disconnect($conn); }
    }
    return $map;
}

// Tách Ngày/Giờ từ chuỗi thời gian (định dạng dd/mm/yyyy và hh:mm:ss)
function split_datetime_display(?string $dt): array {
    $dt = trim((string)$dt);
    if ($dt === '') return ['', ''];
    $ts = strtotime($dt);
    if ($ts === false) return [$dt, ''];
    return [date('d/m/Y', $ts), date('H:i:s', $ts)];
}

// Sắp xếp dữ liệu theo: Mã nhân viên (asc) -> Ngày (asc) -> Giờ (asc)
function sort_attendance_logs_by_pin_date_time(array $rows): array {
    usort($rows, function($a, $b){
        $pa = (string)($a['pin'] ?? '');
        $pb = (string)($b['pin'] ?? '');
        if ($pa !== $pb) return strcmp($pa, $pb);
        $dta = (string)($a['datetime'] ?? '');
        $dtb = (string)($b['datetime'] ?? '');
        $ta = strtotime($dta);
        $tb = strtotime($dtb);
        if ($ta === false) $ta = 0;
        if ($tb === false) $tb = 0;
        if ($ta === $tb) return 0;
        return ($ta < $tb) ? -1 : 1;
    });
    return $rows;
}

// XỬ LÝ THÊM MỚI THỦ CÔNG
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['attendance_create'])) {
    $pin  = isset($_POST['att_pin']) ? format_pin($_POST['att_pin']) : '';
    $name = trim((string)($_POST['att_name'] ?? ''));
    $date = trim((string)($_POST['att_date'] ?? ''));
    $time = trim((string)($_POST['att_time'] ?? ''));

    $errors = [];
    if ($pin === '')  { $errors[] = 'Mã nhân viên không được để trống.'; }
    if ($name === '') { $errors[] = 'Họ tên không được để trống.'; }
    if ($date === '') { $errors[] = 'Ngày không được để trống.'; }

    if (!empty($errors)) {
        $__att_create_error = implode('<br>', $errors);
    } else {
        // Ghép datetime từ ngày + giờ (ưu tiên chuẩn Y-m-d H:i:s)
        $datetime = '';
        if ($date !== '' && $time !== '') {
            $datetime = date('Y-m-d H:i:s', strtotime($date . ' ' . $time));
        } elseif ($date !== '') {
            // Cho phép giờ trống, mặc định 00:00:00
            $datetime = date('Y-m-d 00:00:00', strtotime($date));
        }

        // Tạo record và thêm vào session
        $new = [
            'pin'      => $pin,
            'name'     => $name,
            'datetime' => $datetime,
            'date'     => split_datetime_display($datetime)[0],
            'time'     => ($time === '' ? '' : split_datetime_display($datetime)[1]),
            'verify'   => '-----',
        ];

        $cur = $_SESSION['attendance_logs'] ?? [];
        $cur[] = $new;
        $cur = sort_attendance_logs_by_pin_date_time($cur);
        $_SESSION['attendance_logs'] = $cur;
        $logs = $cur; // cập nhật biến hiển thị ngay

        $__att_created = true;
        $__att_created_msg = 'Đã thêm một dòng thủ công.';
    }
}

// XỬ LÝ CHỈNH SỬA THỜI GIAN (chỉ cho phép sửa Giờ, giữ nguyên Ngày)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['attendance_edit'])) {
    $pin  = isset($_POST['att_edit_pin']) ? format_pin($_POST['att_edit_pin']) : '';
    $time = trim((string)($_POST['att_edit_time'] ?? ''));

    $errors = [];
    if ($pin === '')  { $errors[] = 'Mã nhân viên không được để trống.'; }

    if (!empty($errors)) {
        $__att_edit_error = implode('<br>', $errors);
    } else {
        // Tìm record theo pin và datetime
        $cur = $_SESSION['attendance_logs'] ?? [];
        $oldDatetime = isset($_POST['att_edit_old_datetime']) ? trim((string)$_POST['att_edit_old_datetime']) : '';
        
        $found = false;
        foreach ($cur as $i => $r) {
            if (($r['pin'] ?? '') === $pin && ($r['datetime'] ?? '') === $oldDatetime) {
                // Lấy Ngày cũ (ISO) từ old datetime
                $oldDateIso = date('Y-m-d', strtotime($oldDatetime));
                // Nếu người dùng cung cấp giờ mới thì ghép vào; nếu để trống thì set 00:00:00
                if ($time !== '') {
                    $newDatetime = date('Y-m-d H:i:s', strtotime($oldDateIso . ' ' . $time));
                } else {
                    $newDatetime = date('Y-m-d 00:00:00', strtotime($oldDateIso));
                }
                
                $cur[$i]['datetime'] = $newDatetime;
                $cur[$i]['date'] = split_datetime_display($newDatetime)[0];
                $cur[$i]['time'] = ($time === '' ? '' : split_datetime_display($newDatetime)[1]);
                $cur[$i]['verify'] = '-----';
                $found = true;
                break;
            }
        }
        
        if ($found) {
            $cur = sort_attendance_logs_by_pin_date_time($cur);
            $_SESSION['attendance_logs'] = $cur;
            $logs = $cur;
            $__att_edited = true;
            $__att_edited_msg = 'Đã cập nhật giờ chấm công.';
        } else {
            $__att_edit_error = 'Không tìm thấy bản ghi để chỉnh sửa.';
        }
    }
}

// XỬ LÝ NÚT LẤY DỮ LIỆU
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['fetch_attendance'])) {
    // Cho phép nhập IP/Port từ form, mặc định theo yêu cầu
    $deviceIp   = (isset($_POST['device_ip']) && filter_var($_POST['device_ip'], FILTER_VALIDATE_IP)) ? $_POST['device_ip'] : '192.168.1.227';
    $devicePort = (isset($_POST['device_port']) && (int)$_POST['device_port'] > 0) ? (int)$_POST['device_port'] : 4370;

    // Lấy tên máy theo IP/PORT, fallback về dữ liệu form/selection
    $deviceName = '';
    try {
        $connTmp = db_connect();
        if ($connTmp) {
            $stmtTmp = sqlsrv_query($connTmp, 'SELECT TOP 1 Name FROM ATTManager WHERE IP = ? AND PORT = ?', [$deviceIp, $devicePort]);
            if ($stmtTmp !== false) {
                $rowTmp = sqlsrv_fetch_array($stmtTmp, SQLSRV_FETCH_ASSOC);
                if ($rowTmp && isset($rowTmp['Name'])) $deviceName = (string)$rowTmp['Name'];
                sqlsrv_free_stmt($stmtTmp);
            }
            db_disconnect($connTmp);
        }
    } catch (Throwable $e) { /* ignore */ }
    if ($deviceName === '' && isset($_POST['device_name'])) {
        $deviceName = (string)$_POST['device_name'];
    }
    if ($deviceName === '' && !empty($selectedDevice['name'])) {
        $deviceName = (string)$selectedDevice['name'];
    }

    $logs = [];
    $error = null;

    // Luôn dùng UDP (ZKLibrary)
    require_once __DIR__ . '/lib/ZKLibrary.php';
    $zk = new ZKLibrary($deviceIp, $devicePort);
    if (method_exists($zk, 'setTimeout')) { $zk->setTimeout(5, 0); }

    // Đọc phạm vi thời gian người dùng chọn (tùy chọn)
    $fromDate = isset($_POST['fetch_from']) ? trim((string)$_POST['fetch_from']) : '';
    $toDate   = isset($_POST['fetch_to'])   ? trim((string)$_POST['fetch_to'])   : '';
    $fromTs = $fromDate ? strtotime($fromDate . ' 00:00:00') : null;
    $toTs   = $toDate   ? strtotime($toDate   . ' 23:59:59') : null;

    if ($zk->connect()) {
        $zk->disableDevice();
        $raw = $zk->getAttendance();
        $zk->enableDevice();
        $zk->disconnect();

        if (is_array($raw)) {
            foreach ($raw as $r) {
                // ZKLibrary trả: [$uid, $id, $state, $timestamp]
                $dtStr = isset($r[3]) ? (string)$r[3] : '';
                $ts = $dtStr ? strtotime($dtStr) : null;
                if ($fromTs && $ts && $ts < $fromTs) continue;
                if ($toTs && $ts && $ts > $toTs) continue;
                $logs[] = [
                    'pin'      => isset($r[1]) ? format_pin((string)$r[1]) : '',
                    'verify'   => isset($r[2]) ? (string)$r[2] : '',
                    'datetime' => $dtStr,
                    'date'     => split_datetime_display($dtStr)[0],
                    'time'     => split_datetime_display($dtStr)[1],
                    'device_name' => (string)$deviceName,
                ];
            }
        }
        if (!$logs) {
            $error = 'Không nhận được dữ liệu chấm công nào từ thiết bị (UDP).';
        } else {
            $__att_fetch_done = true;
            $__att_success_count = count($logs);
            $__att_device = $_SESSION['attendance_device'] ?? ($selectedDevice['name'] ?? '');
            $__att_device_ipport = $deviceIp . ':' . $devicePort;
        }
    } else {
        $errno  = method_exists($zk, 'getLastErrno') ? ($zk->getLastErrno() ?? '-') : '-';
        $errstr = method_exists($zk, 'getLastError') ? ($zk->getLastError() ?? 'Không rõ lỗi') : 'Không rõ lỗi';
        $hint   = '';
        if ($errno === 10013) { $hint = ' (bị chặn bởi tường lửa/permission denied)'; }
        if ($errno === 10060) { $hint = ' (hết thời gian chờ/timeout)'; }
        $error = 'Không thể kết nối UDP tới thiết bị (' . $deviceIp . ':' . $devicePort . '). Lỗi: ' . $errstr . ' [errno=' . $errno . ']' . $hint;
    }

    // Ghép họ tên theo mã NV
    if (!empty($logs)) {
        $pinList = array_column($logs, 'pin');
        $nameMap = fetch_employee_names($pinList);
        foreach ($logs as $i => $r) {
            $p = $r['pin'] ?? '';
            $logs[$i]['name'] = $nameMap[$p] ?? '';
        }
    }

    // Sắp xếp theo: Mã NV -> Ngày/Giờ
    $logs = sort_attendance_logs_by_pin_date_time($logs);

    // Lưu dữ liệu vào biến/phiên nhưng KHÔNG redirect (tránh lỗi headers sent)
    $_SESSION['attendance_logs'] = $logs; // cho phép rời trang vẫn còn dữ liệu
    $_SESSION['attendance_last_error'] = $error;
    // Không header redirect; để render thẳng dưới đây
}

// Tuỳ chọn số dòng mỗi trang (giống công văn đến)
$allowedPageSizes = [5, 10, 20, 50, 100];
$pageSize = isset($_GET['page_size']) ? (int)$_GET['page_size'] : (isset($_SESSION['attendance_page_size']) ? (int)$_SESSION['attendance_page_size'] : 5);
if (!in_array($pageSize, $allowedPageSizes, true)) { $pageSize = 5; }
$_SESSION['attendance_page_size'] = $pageSize;

// Phân trang tương tự trang công văn đến
$itemsPerPage = $pageSize;
$currentPage = (int)($_GET['page'] ?? 1);
$currentPage = max(1, $currentPage);
$totalItems = is_array($logs) ? count($logs) : 0;
$totalPages = ($itemsPerPage > 0) ? (int)ceil($totalItems / $itemsPerPage) : 1;
$offset = ($currentPage - 1) * $itemsPerPage;
$pageLogs = array_slice($logs, $offset, $itemsPerPage);
$verifyMap = [];
try {
    $conn = db_connect();
    if ($conn) {
        $stmt = sqlsrv_query($conn, 'SELECT Id, Name_VN, Name_ENG FROM ATT_Type_Connect');
        if ($stmt !== false) {
            $isEng = (($_SESSION['lang'] ?? 'vn') === 'eng');
            while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
                $id = (string)($row['Id'] ?? '');
                $label = $isEng ? (string)($row['Name_ENG'] ?? '') : (string)($row['Name_VN'] ?? '');
                if ($id !== '') { $verifyMap[$id] = $label; }
            }
            sqlsrv_free_stmt($stmt);
        }
        db_disconnect($conn);
    }
} catch (Throwable $e) { /* ignore */ }
?>

<section class="panel-grid attendance-grid">
    <div class="panel wide attendance-panel">
        <header class="panel-header">
            <h3>Dữ liệu chấm công</h3>
            <style>
            /* Attendance page scoped styles - do not touch global CSS */
            .attendance-panel { overflow-x:hidden; }
            .attendance-panel .panel-header { overflow-x:hidden; }
            .attendance-panel .attendance-controls { display:flex; gap:12px; flex-wrap:nowrap; justify-content:flex-end; align-items:center; overflow-x:hidden; }
            .attendance-panel #attendance-fetch-form { display:flex; gap:12px; align-items:center; }
            .attendance-panel #attendance-fetch-form > * { min-width:0; }
            .attendance-panel .attendance-btn { padding:8px 16px !important; font-size:13px !important; font-weight:500 !important; border-radius:8px !important; min-height:36px !important; display:inline-flex !important; align-items:center !important; justify-content:center !important; gap:8px !important; white-space:nowrap !important; text-decoration:none !important; background:linear-gradient(135deg, rgba(100,167,166,0.9), rgba(122,220,201,0.95)) !important; color:#052729 !important; border:1px solid rgba(122,220,201,0.4) !important; }
            .attendance-panel .attendance-btn:hover { background:linear-gradient(135deg, rgba(122,220,201,0.95), rgba(100,167,166,0.9)) !important; transform:translateY(-1px) !important; box-shadow:0 4px 12px rgba(122,220,201,0.3) !important; }
            .attendance-panel .attendance-controls .btn-icon-wrapper { display:inline-grid; place-items:center; width:14px; height:14px; }
            .attendance-panel .attendance-controls .btn-icon-wrapper svg { width:14px; height:14px; }
            .attendance-panel .attendance-controls .btn-text-wrapper { font-size:13px; font-weight:500; color:inherit; }
            .attendance-panel .attendance-btn:focus-visible { outline:2px solid rgba(122,220,201,0.6) !important; outline-offset:2px !important; }
            .attendance-panel #attendance-device-custom-select { width:auto; min-width:0; }
            .attendance-panel #attendance-device-custom-select .custom-select-toggle { max-width:100%; }
            .attendance-panel #attendance-device-custom-select .custom-select-menu { z-index: 20; }

            /* Mobile fixes */
            @media (max-width: 768px) {
              .attendance-panel .panel-header { display:flex; flex-direction:column; align-items:stretch; }
              .attendance-panel .attendance-controls { width: 100%; display:block !important; }
              /* Custom Select ở hàng riêng; 2 nút ở hàng kế tiếp */
              .attendance-panel #attendance-fetch-form {
                width: 100% !important;
                display: grid !important;
                grid-template-columns: 1fr 1fr !important;
                gap: 8px !important;
                align-items: stretch !important;
              }
              .attendance-panel #attendance-device-custom-select { grid-column: 1 / -1 !important; width: 100%; min-width: 0; }
              .attendance-panel #attendance-device-custom-select .custom-select-toggle { width: 100%; }
              .attendance-panel #attendance-device-custom-select .custom-select-toggle span { display:flex; flex-direction:column; overflow:hidden; }
              .attendance-panel #attendance-device-custom-select .custom-select-toggle span > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
              .attendance-panel #attendance-device-custom-select .custom-select-menu { width: 100%; min-width: 100%; }
              .attendance-panel #attendance-fetch-form .attendance-btn { width: 100% !important; font-size:13px !important; padding:8px 10px !important; position:relative !important; float:none !important; align-self:stretch !important; display:grid !important; grid-template-rows: 18px 1fr !important; align-items:center !important; justify-items:center !important; gap:4px !important; line-height:1.3 !important; min-height:60px !important; height:100% !important; overflow:visible !important; }
              .attendance-panel #attendance-open-create { width: 100% !important; margin-top: 0 !important; }
              /* Allow full label, avoid clipping */
              .attendance-panel #attendance-fetch-form .attendance-btn .btn-text-wrapper { white-space:normal !important; overflow:visible !important; text-overflow:unset !important; text-align:center !important; display:block !important; width:100% !important; }
              .attendance-panel #attendance-fetch-form .attendance-btn .btn-icon-wrapper { width:18px !important; height:18px !important; margin:0 !important; display:block !important; }
            }
            @media (max-width: 390px) {
              .attendance-panel #attendance-device-custom-select .custom-select-toggle { padding: 6px 10px; }
            }
            </style>
            <style>
            /* Additional strict mobile fixes */
            @media (max-width: 768px) {
              .attendance-panel .panel-header { display:block; }
              .attendance-panel .attendance-controls { width: 100% !important; justify-content: flex-start !important; overflow:hidden; }
              .attendance-panel #attendance-fetch-form { width: 100% !important; display:grid !important; grid-template-columns: 1fr 1fr !important; gap:8px !important; align-items:stretch !important; }
              .attendance-panel #attendance-device-custom-select { width: 100% !important; min-width: 0 !important; display:block; }
              .attendance-panel #attendance-device-custom-select .custom-select-toggle { width: 100% !important; }
              .attendance-panel #attendance-fetch-form .attendance-btn { width: 100% !important; display:inline-flex !important; margin-top:0 !important; }
              .attendance-panel .attendance-controls, 
              .attendance-panel #attendance-fetch-form,
              .attendance-panel #attendance-device-custom-select,
              .attendance-panel #attendance-device-custom-select .custom-select-toggle,
              .attendance-panel .attendance-btn { box-sizing: border-box; max-width:100%; }
            }
            .att-actions { display:flex; gap:8px;     justify-content: center; }
            .att-btn-icon { background:transparent; border:1px solid rgba(122,220,201,.28); color:#bfeee3; border-radius:6px; width:28px; height:28px; display:inline-grid; place-items:center; cursor:pointer; transition:.2s; }
            .att-btn-icon:hover { background:rgba(122,220,201,.12); color:#7adcc9; border-color:rgba(122,220,201,.45); }
            </style>
            <div class="panel-controls attendance-controls">

                <?php
                // Load time clock devices
                $devices = [];
                try {
                    $conn = db_connect();
                    if ($conn) {
                        $stmt = sqlsrv_query($conn, 'SELECT Name, IP, PORT FROM ATTManager ORDER BY Name ASC');
                        if ($stmt !== false) {
                            while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
                                $devices[] = [
                                    'key' => (string)($row['Name'] ?? ''),
                                    'name' => (string)($row['Name'] ?? ''),
                                    'ip' => (string)($row['IP'] ?? ''),
                                    'port' => (int)($row['PORT'] ?? 4370),
                                ];
                            }
                            sqlsrv_free_stmt($stmt);
                        }
                        db_disconnect($conn);
                    }
                } catch (Throwable $e) { /* ignore */ }
                // Chọn thiết bị mặc định theo Name (ATTManager không có Id)
                $selectedKey = isset($_GET['device']) ? (string)$_GET['device'] : (isset($_SESSION['attendance_device']) ? (string)$_SESSION['attendance_device'] : (count($devices) ? (string)$devices[0]['key'] : ''));
                if (isset($_GET['device'])) { $_SESSION['attendance_device'] = $selectedKey; }
                $selectedDevice = null;
                foreach ($devices as $d) { if ((($d['key'] ?? $d['name']) === $selectedKey)) { $selectedDevice = $d; break; } }
                if ($selectedDevice === null && count($devices)) { $selectedDevice = $devices[0]; $selectedKey = $selectedDevice['key'] ?? $selectedDevice['name']; }
                $uiIp = isset($_POST['device_ip']) ? (string)$_POST['device_ip'] : ($selectedDevice['ip'] ?? '192.168.1.227');
                $uiPort = isset($_POST['device_port']) ? (int)$_POST['device_port'] : ($selectedDevice['port'] ?? 4370);
                ?>
                <form id="attendance-fetch-form" method="post" class="inline-form" style="display:flex; gap:12px; align-items:center; flex-wrap:nowrap;">
                    <div class="custom-select" id="attendance-device-custom-select">
                        <button type="button" class="custom-select-toggle" style="min-width:240px; display:flex; justify-content:space-between; align-items:center; padding:8px 12px;" data-placeholder="<?= htmlspecialchars($selectedDevice['name'] ?? 'Chọn máy chấm công', ENT_QUOTES, 'UTF-8') ?>">
                            <span style="display:flex; flex-direction:column; align-items:flex-start; gap:2px;">
                                <span style="font-size:13px; font-weight:500;"><?= htmlspecialchars($selectedDevice['name'] ?? 'Chọn máy chấm công', ENT_QUOTES, 'UTF-8') ?></span>
                                <span style="font-size:11px; color:#7eb8b0;" id="attendance-info-display"><?= htmlspecialchars($uiIp . ':' . $uiPort, ENT_QUOTES, 'UTF-8') ?></span>
                            </span>
                            <svg style="width:16px; height:16px; fill:currentColor; flex-shrink:0;" viewBox="0 0 24 24"><path d="M7 10l5 5 5-5z"/></svg>
                        </button>
                        <div class="custom-select-menu" role="listbox">
                            <?php foreach ($devices as $dev): ?>
                                <div class="custom-select-option<?= ((($dev['key'] ?? $dev['name']) === $selectedKey) ? ' is-selected' : '') ?>" role="option" data-value="<?= htmlspecialchars($dev['key'] ?? $dev['name'], ENT_QUOTES, 'UTF-8') ?>" data-ip="<?= htmlspecialchars($dev['ip'], ENT_QUOTES, 'UTF-8') ?>" data-port="<?= (int)$dev['port'] ?>" style="padding:10px 12px;">
                                    <div style="font-size:13px; font-weight:500;"><?= htmlspecialchars($dev['name'], ENT_QUOTES, 'UTF-8') ?></div>
                                    <div style="font-size:11px; color:#7eb8b0; margin-top:2px;">&lrm;<?= htmlspecialchars($dev['ip'] . ':' . $dev['port'], ENT_QUOTES, 'UTF-8') ?></div>
                                </div>
                            <?php endforeach; ?>
                        </div>
                        <input type="hidden" id="attendance-device-key" value="<?= htmlspecialchars($selectedKey, ENT_QUOTES, 'UTF-8') ?>" />
                    </div>
                    <input id="attendance-ip" type="hidden" name="device_ip" value="<?= htmlspecialchars($uiIp, ENT_QUOTES, 'UTF-8') ?>" />
                    <input id="attendance-port" type="hidden" name="device_port" value="<?= htmlspecialchars((string)$uiPort, ENT_QUOTES, 'UTF-8') ?>" />
                    <input id="attendance-device-name" type="hidden" name="device_name" value="<?= htmlspecialchars($selectedDevice['name'] ?? '', ENT_QUOTES, 'UTF-8') ?>" />
                    <input type="hidden" id="attendance-fetch-from" name="fetch_from" value="" />
                    <input type="hidden" id="attendance-fetch-to" name="fetch_to" value="" />
                    <input type="hidden" id="attendance-fetch-flag" name="fetch_attendance" value="" />
                    <input type="hidden" id="attendance-fetch-all" name="fetch_all" value="" />
                    <button type="button" id="attendance-open-fetch" class="btn btn-primary attendance-btn">
                        <span aria-hidden="true" class="btn-icon-wrapper">
                            <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <path d="M12 3v12"></path>
                                <path d="M8 11l4 4 4-4"></path>
                                <path d="M5 21h14"></path>
                            </svg>
                        </span>
                        <span class="btn-text-wrapper">Lấy dữ liệu từ máy</span>
                    </button>
                    <button type="button" class="btn btn-primary attendance-btn" id="attendance-open-create">
                        <span aria-hidden="true" class="btn-icon-wrapper">
                            <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <line x1="12" y1="5" x2="12" y2="19"></line>
                                <line x1="5" y1="12" x2="19" y2="12"></line>
                            </svg>
                        </span>
                        <span class="btn-text-wrapper">Thêm mới</span>
                    </button>
                    
                </form>
                <style id="attendance-sprin-fix">
                  /* Do not clip the header controls */
                  .attendance-panel, .attendance-panel .panel-header, .attendance-panel .attendance-controls { overflow: visible !important; }
                  /* Always hide old buttons */
                  .attendance-panel #attendance-open-fetch, .attendance-panel #attendance-open-create { display:none !important; }
                  /* Place and lift the sprint menu */
                  .attendance-panel .menu-sprin-wrapper { position: relative !important; display:inline-block !important; z-index: 100000 !important; }
                  .attendance-panel .menu-sprin-btn, .attendance-panel .menu-sprin-item { z-index: 100001 !important; }
                  /* Base (closed) state for items */
                  .attendance-panel .menu-sprin-wrapper .menu-sprin-item { opacity:0; pointer-events:none; transform: scale(.6); transition: transform .25s cubic-bezier(.68,-.55,.27,1.55), opacity .18s ease; }
                  /* Open state controlled by wrapper.open class (JS toggles) */
                  .attendance-panel .menu-sprin-wrapper.open .item1 { transform: translate(0,56px) scale(1);   opacity:1; pointer-events:auto; }
                  .attendance-panel .menu-sprin-wrapper.open .item2 { transform: translate(-46px,46px) scale(1); opacity:1; pointer-events:auto; }
                  .attendance-panel .menu-sprin-wrapper.open .item3 { transform: translate(-56px,0)  scale(1);  opacity:1; pointer-events:auto; }
                  .attendance-panel .menu-sprin-wrapper.open .item4 { transform: translate(-46px,-46px) scale(1);opacity:1; pointer-events:auto; }
                  .attendance-panel .menu-sprin-wrapper.open .item5 { transform: translate(0,-56px)  scale(1);  opacity:1; pointer-events:auto; }
                </style>
                <div class="menu-sprin-wrapper">
                    <!-- Checkbox điều khiển trạng thái mở/đóng -->
                    <input type="checkbox" id="menu-sprin-toggle" class="menu-sprin-toggle" />

                    <!-- Nút trung tâm: dùng emoji để khỏi cần CDN -->
                    <label class="menu-sprin-btn" for="menu-sprin-toggle" aria-label="Mở menu">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                        <path d="M3 11l9-8 9 8"/>
                        <path d="M9 22V12h6v10"/>
                    </svg>
                    </label>

                    <!-- Các nút con: cũng dùng emoji, sau này bạn thay bằng icon FontAwesome/SVG tuỳ ý -->
                    <div class="menu-sprin-item item1" role="button" aria-label="Tài khoản">
                    <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                        <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
                        <circle cx="12" cy="7" r="4"/>
                    </svg>
                    </div>
                    <div class="menu-sprin-item item2" role="button" aria-label="Đào tạo">
                    <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                        <path d="M22 10l-10-5-10 5 10 5 10-5z"/>
                        <path d="M6 12v5c3 3 9 3 12 0v-5"/>
                        <path d="M12 5v5"/>
                    </svg>
                    </div>
                    <div class="menu-sprin-item item3" role="button" aria-label="Thiết bị">
                    <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                        <rect x="3" y="5" width="18" height="12" rx="2"/>
                        <path d="M2 19h20"/>
                    </svg>
                    </div>
                    <div class="menu-sprin-item item4" role="button" aria-label="Công cụ">
                    <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                        <path d="M21.7 13.4l-3.4 3.4-7.1-7.1 3.4-3.4a4 4 0 0 1 7.1 3.4l-3 3z" opacity=".2"/>
                        <path d="M14.7 6.3a4.5 4.5 0 1 0 3 3l3.3-3.3a2 2 0 1 0-2.8-2.8L14.7 6.3z"/>
                        <path d="M16.2 11.8L7.5 20.5a2.5 2.5 0 0 1-3.5-3.5l8.7-8.7"/>
                    </svg>
                    </div>
                    <div class="menu-sprin-item item5" role="button" aria-label="Thư">
                    <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                        <rect x="3" y="5" width="18" height="14" rx="2"/>
                        <path d="M3 7l9 6 9-6"/>
                    </svg>
                    </div>
                </div>

                <script>
                // Sprint menu: simple toggle via checkbox -> wrapper.open
                (function(){
                  var wrap = document.querySelector('.attendance-panel .menu-sprin-wrapper');
                  var toggle = document.getElementById('menu-sprin-toggle');
                  if (!wrap || !toggle) return;
                  function sync(){ wrap.classList.toggle('open', !!toggle.checked); }
                  // initialize and bind
                  sync();
                  toggle.addEventListener('change', sync);
                  // Map actions to hidden buttons
                  function fire(id){ var el=document.getElementById(id); if (el) el.click(); }
                  var itemFetch  = wrap.querySelector('.item1');
                  var itemCreate = wrap.querySelector('.item2');
                  if (itemFetch){ itemFetch.addEventListener('click', function(){ toggle.checked=false; sync(); fire('attendance-open-fetch'); }); }
                  if (itemCreate){ itemCreate.addEventListener('click', function(){ toggle.checked=false; sync(); fire('attendance-open-create'); }); }
                })();
                </script>

                <!-- Fetch Range Modal -->
                <div id="attendance-fetch-modal" class="modal-backdrop" aria-hidden="true">
                    <div class="modal">
                        <div class="modal-header">
                            <h4>LỌC THỜI GIAN</h4>
                            <button type="button" class="modal-close" data-modal-close>&times;</button>
                        </div>
                        <div class="modal-body">
                            <form id="attendance-fetch-range-form" class="form-grid grid-2" onsubmit="return false;">
                                <div class="form-row">
                                    <label for="att-fetch-from">Từ ngày</label>
                                    <div class="input-wrap">
                                        <span class="icon date-icon" data-target="att-fetch-from">
                                            <svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
                                        </span>
                                        <input type="date" id="att-fetch-from" autocomplete="off" style="font-size:16px;">
                                    </div>
                                </div>
                                <div class="form-row">
                                    <label for="att-fetch-to">Đến ngày</label>
                                    <div class="input-wrap">
                                        <span class="icon date-icon" data-target="att-fetch-to">
                                            <svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
                                        </span>
                                        <input type="date" id="att-fetch-to" autocomplete="off" style="font-size:16px;">
                                    </div>
                                </div>
                                <div class="form-row full">
                                    <label></label>
                                    <div class="input-wrap" style="display:flex; align-items:center; gap:8px; border:none; background:transparent; padding:0;font-size: 12px;justify-content: center;font-family: monospace;">
                                        <input type="checkbox" id="att-fetch-all" style="width:16px;height:16px;">
                                        <span>Tất cả thời gian</span>
                                    </div>
                                </div>
                                <div class="form-actions full">
                                    <button type="button" class="btn btn-primary" id="attendance-fetch-apply">Tải dữ liệu</button>
                                    <button type="button" class="btn btn-secondary" data-modal-close>Hủy</button>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>

                <style>
                  /* Strong mobile-friendly sizing for the fetch range modal */
                  #attendance-fetch-modal { position: fixed !important; inset: 0 !important; display: none; align-items: center !important; justify-content: center !important; overflow: auto !important; z-index: 100000 !important; padding: 12px 12px max(16px, env(safe-area-inset-bottom)) 12px !important; }
                  #attendance-fetch-modal .modal { width: min(560px, calc(100% - 24px)) !important; max-width: 100% !important; max-height: calc(100dvh - 24px) !important; height: auto !important; box-sizing: border-box !important; overflow: auto !important; }
                  @supports not (height: 100dvh) {
                    #attendance-fetch-modal .modal { max-height: calc(100vh - 24px) !important; }
                  }
                  #attendance-fetch-modal .modal-body { flex: 1 1 auto !important; overflow: auto !important; padding-bottom: 20px !important; -webkit-overflow-scrolling: touch !important; }
                  #attendance-fetch-modal .form-actions { position: sticky !important; bottom: 0 !important; background: rgba(12,38,43,.00) !important; padding-top: 10px !important; margin-top: 10px !important; z-index: 1 !important; }
                  /* If viewport height is small, anchor to top so footer buttons are reachable */
                  @media (max-height: 700px) {
                    #attendance-fetch-modal { align-items: flex-start !important; }
                    #attendance-fetch-modal .modal { margin-top: 12px !important; }
                  }
                  /* Keep action buttons visible on narrow devices */
                  @media (max-width: 480px) {
                    #attendance-fetch-modal .form-actions { position: sticky !important; bottom: 0 !important; background: rgba(12,38,43,.00) !important; padding-top: 10px !important; margin-top: 10px !important; z-index: 1 !important; }
                  }
                </style>

                <script>
                (function(){
                    var openFetchBtn = document.getElementById('attendance-open-fetch');
                    var fetchModal = document.getElementById('attendance-fetch-modal');
                    var fetchApply = document.getElementById('attendance-fetch-apply');
                    var fetchFrom = document.getElementById('att-fetch-from');
                    var fetchTo = document.getElementById('att-fetch-to');
                    var hiddenFrom = document.getElementById('attendance-fetch-from');
                    var hiddenTo = document.getElementById('attendance-fetch-to');
                    var hiddenFlag = document.getElementById('attendance-fetch-flag');
                    var hiddenAll = document.getElementById('attendance-fetch-all');
                    var chkAll = document.getElementById('att-fetch-all');
                    var fetchForm = document.getElementById('attendance-fetch-form');

                    function applyAllState(){
                        var all = !!(chkAll && chkAll.checked);
                        if (fetchFrom) fetchFrom.disabled = all;
                        if (fetchTo) fetchTo.disabled = all;
                        if (hiddenAll) hiddenAll.value = all ? '1' : '';
                    }

                    function openFetch(){
                        if (!fetchModal) return;
                        // Move modal to body to avoid clipping by transformed/overflow parents
                        try {
                          if (!fetchModal.__portaled) {
                            document.body.appendChild(fetchModal);
                            fetchModal.__portaled = true;
                          }
                        } catch(_){}
                        // Prefill last 7 days by default if empty
                        try{
                          var today = new Date();
                          var toStr = today.toISOString().slice(0,10);
                          var fromD = new Date(today); fromD.setDate(fromD.getDate()-6);
                          var fromStr = fromD.toISOString().slice(0,10);
                          if (fetchFrom && !fetchFrom.value) fetchFrom.value = fromStr;
                          if (fetchTo && !fetchTo.value) fetchTo.value = toStr;
                        }catch(_){}
                        applyAllState();
                        fetchModal.style.position='fixed';
                        fetchModal.style.inset='0';
                        fetchModal.style.zIndex='2147483000';
                        fetchModal.classList.add('show');
                        fetchModal.style.display='flex';
                        fetchModal.setAttribute('aria-hidden','false');
                        setTimeout(function(){ if(fetchFrom && !(chkAll && chkAll.checked)){ try{ fetchFrom.focus(); }catch(_){}} }, 30);
                    }
                    function closeFetch(){ if(!fetchModal) return; fetchModal.classList.remove('show'); fetchModal.style.display='none'; fetchModal.setAttribute('aria-hidden','true'); }
                    if (openFetchBtn) openFetchBtn.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); openFetch(); });
  // Fallback delegated binding in case the button is re-rendered
  document.addEventListener('click', function(e){ var t=e.target && e.target.closest ? e.target.closest('#attendance-open-fetch') : null; if (t){ e.preventDefault(); e.stopPropagation(); openFetch(); }}, true);
                    if (chkAll){ chkAll.addEventListener('change', applyAllState); }
                    if (fetchApply){
                        fetchApply.addEventListener('click', function(){
                            var all = !!(chkAll && chkAll.checked);
                            var fv = fetchFrom ? fetchFrom.value : '';
                            var tv = fetchTo ? fetchTo.value : '';
                            if (!all && fv && tv && fv > tv){
                                if (window.showToast) window.showToast('error', '<div class="error-header">THÔNG BÁO</div>Từ ngày phải nhỏ hơn hoặc bằng Đến ngày');
                                return;
                            }
                            if (hiddenFrom) hiddenFrom.value = all ? '' : fv;
                            if (hiddenTo) hiddenTo.value = all ? '' : tv;
                            if (hiddenFlag) hiddenFlag.value = '1';
                            if (hiddenAll) hiddenAll.value = all ? '1' : '';
                            if (fetchForm) fetchForm.submit();
                            closeFetch();
                        });
                    }
                    if (fetchModal){
                        fetchModal.querySelectorAll('[data-modal-close]').forEach(function(btn){ btn.addEventListener('click', closeFetch); });
                        fetchModal.addEventListener('click', function(e){ if (e.target === fetchModal) closeFetch(); });
                        document.addEventListener('keydown', function(e){ if (fetchModal.classList.contains('show') && e.key==='Escape') closeFetch(); });
                    }

                    // Init date picker for fetch modal
                    try{ var m = document.getElementById('attendance-fetch-modal'); if (m && typeof setupDatePickerForModal==='function') setupDatePickerForModal(m); }catch(_){}

                    var custom = document.getElementById('attendance-device-custom-select');
                    if (!custom) return;
                    var toggle = custom.querySelector('.custom-select-toggle');
                    var menu = custom.querySelector('.custom-select-menu');
                    var ipInput = document.getElementById('attendance-ip');
                    var portInput = document.getElementById('attendance-port');
                    var infoDisplay = document.getElementById('attendance-info-display');
                    if (!toggle || !menu) return;

                    var anchorParent = null, anchorNext = null, isPortaled = false;
                    var closeOnScroll = null, closeOnResize = null;

                    function mountMenu(){
                        if (isPortaled) return;
                        anchorParent = menu.parentNode;
                        anchorNext = menu.nextSibling;
                        document.body.appendChild(menu);
                        isPortaled = true;
                    }
                    function unmountMenu(){
                        if (!isPortaled) return;
                        if (anchorParent){
                            if (anchorNext){ anchorParent.insertBefore(menu, anchorNext); }
                            else { anchorParent.appendChild(menu); }
                        }
                        isPortaled = false;
                    }
                    function positionMenu(){
                        var r = toggle.getBoundingClientRect();
                        var pad = 12;
                        var maxH = Math.min(320, Math.max(200, window.innerHeight - r.bottom - 16));
                        var left = Math.round(r.left);
                        var width = Math.round(r.width);
                        if (left < pad) left = pad;
                        if (left + width + pad > window.innerWidth){
                            left = Math.max(pad, window.innerWidth - width - pad);
                        }
                        menu.style.position = 'fixed';
                        menu.style.left = left + 'px';
                        menu.style.top  = Math.round(r.bottom + 6) + 'px';
                        menu.style.width = width + 'px';
                        menu.style.minWidth = width + 'px';
                        menu.style.maxWidth = width + 'px';
                        menu.style.maxHeight = maxH + 'px';
                        menu.style.overflowY = 'auto';
                        menu.style.zIndex = '2147483000';
                        menu.style.display = 'block';
                    }
                    function clearMenuStyles(){
                        menu.style.position = '';
                        menu.style.left = '';
                        menu.style.top = '';
                        menu.style.width = '';
                        menu.style.minWidth = '';
                        menu.style.maxWidth = '';
                        menu.style.maxHeight = '';
                        menu.style.overflowY = '';
                        menu.style.zIndex = '';
                        menu.style.display = '';
                    }
                    function closeAll(){
                        custom.classList.remove('open');
                        clearMenuStyles();
                        unmountMenu();
                        if (closeOnScroll){ window.removeEventListener('scroll', closeOnScroll, true); closeOnScroll = null; }
                        if (closeOnResize){ window.removeEventListener('resize', closeOnResize); closeOnResize = null; }
                    }

                    // prevent page scroll when scrolling inside menu
                    menu.addEventListener('wheel', function(e){ e.stopPropagation(); }, { passive: true });
                    menu.addEventListener('touchmove', function(e){ e.stopPropagation(); }, { passive: true });
                    menu.addEventListener('mousedown', function(e){ e.stopPropagation(); });

                    document.addEventListener('click', function(e){ if (!custom.contains(e.target) && !menu.contains(e.target)) closeAll(); });
                    toggle.addEventListener('click', function(e){
                        e.stopPropagation();
                        var willOpen = !custom.classList.contains('open');
                        custom.classList.toggle('open');
                        if (willOpen){
                            mountMenu();
                            positionMenu();
                            closeOnScroll = function(ev){ if (menu.contains(ev.target)) return; closeAll(); };
                            closeOnResize = function(){ closeAll(); };
                            window.addEventListener('scroll', closeOnScroll, true);
                            window.addEventListener('resize', closeOnResize);
                        } else {
                            closeAll();
                        }
                    });

                    menu.addEventListener('click', function(e){
                        var opt = e.target.closest('.custom-select-option');
                        if (!opt) return;
                        var nameEl = opt.querySelector('div:first-child');
                        var name = nameEl ? nameEl.textContent.trim() : opt.textContent.trim();
                        var ip = opt.getAttribute('data-ip') || '';
                        var port = opt.getAttribute('data-port') || '';
                        var toggleContent = toggle.querySelector('span');
                        if (toggleContent) {
                            toggleContent.innerHTML = '<span style="font-size:13px; font-weight:500;">' + name + '</span><span style="font-size:11px; color:#7eb8b0; margin-top:2px;">' + ip + ':' + port + '</span>';
                        }
                        menu.querySelectorAll('.custom-select-option').forEach(function(el){ el.classList.toggle('is-selected', el===opt); });
                        if (ipInput) ipInput.value = ip;
                        if (portInput) portInput.value = port;
                        var nameHidden = document.getElementById('attendance-device-name');
                        if (nameHidden) nameHidden.value = name;
                        if (infoDisplay) infoDisplay.textContent = ip + ':' + port;
                        // Keep selected device across pagination & page-size
                        try {
                          var devKey = opt.getAttribute('data-value') || '';
                          // update all pagination links
                          document.querySelectorAll('.pagination a').forEach(function(a){
                            var href = a.getAttribute('href') || '';
                            if (!href) return;
                            if (href.indexOf('device=') >= 0) {
                              href = href.replace(/([?&]device=)[^&]*/, '$1' + encodeURIComponent(devKey));
                            } else {
                              href += (href.indexOf('?') >= 0 ? '&' : '?') + 'device=' + encodeURIComponent(devKey);
                            }
                            a.setAttribute('href', href);
                          });
                          // update hidden device in page-size form
                          var devHidden = document.getElementById('attendance-pagesize-device');
                          if (devHidden) devHidden.value = devKey;
                        } catch (e) { /* ignore */ }
                        closeAll();
                    });
                })();
                </script>
            </div>
        </header>


        <?php 
        $__att_err = $error ?? ($_SESSION['attendance_last_error'] ?? null);
        if (!empty($__att_err)): ?>
        <script>
        document.addEventListener('DOMContentLoaded', function(){
            try {
                var msg = <?= json_encode($__att_err, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
                var title = (window.__MESSAGES__ && window.__MESSAGES__['error_notification_title']) ? window.__MESSAGES__['error_notification_title'] : 'THÔNG BÁO';
                if (typeof window.showToast === 'function') {
                    window.showToast('error', '<div class="error-header">' + title + '</div>' + msg);
                }
            } catch (e) {}
        });
        </script>
        <?php $_SESSION['attendance_last_error'] = null; endif; ?>

        <?php if ($__att_fetch_done && $__att_success_count > 0): ?>
        <script>
        document.addEventListener('DOMContentLoaded', function(){
            try {
                var cnt = <?= (int)$__att_success_count ?>;
                var dev = <?= json_encode($__att_device, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
                var ip  = <?= json_encode($__att_device_ipport, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
                var title = (window.__MESSAGES__ && window.__MESSAGES__['success_notification_title']) ? window.__MESSAGES__['success_notification_title'] : 'THÔNG BÁO';
                var body = 'Đã lấy <strong>' + cnt + '</strong> bản ghi từ <strong>' + dev + '</strong> (' + ip + ')';
                if (typeof window.showToast === 'function') {
                    window.showToast('success', '<div class="success-header">' + title + '</div>' + body);
                }
            } catch(e) {}
        });
        </script>
        <?php endif; ?>

        <div class="table-wrapper">
            <table class="data-table data-table--responsive" id="attendance-table">
                <thead>
                    <tr>
                        <th>Mã nhân viên</th>
                        <th>Họ tên</th>
                        <th>Thời gian</th>
                        <th>Ngày</th>
                        <th>Giờ</th>
                        <th>Kiểu xác thực</th>
                        <th>Tên máy</th>
                        <th>Thao tác</th>
                    </tr>
                    </tr>
                </thead>
                <tbody>
                    <?php if (!empty($pageLogs)): ?>
                        <?php foreach ($pageLogs as $row): ?>
                            <tr>
                                <td data-label="Mã nhân viên"><?= htmlspecialchars($row['pin'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
                                <td data-label="Họ tên"><?= htmlspecialchars($row['name'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
                                <td data-label="Thời gian"><?= htmlspecialchars($row['datetime'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
                                <td data-label="Ngày"><?= htmlspecialchars($row['date'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
                                <td data-label="Giờ"><?= htmlspecialchars($row['time'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
                                <td data-label="Kiểu xác thực"><?= htmlspecialchars($verifyMap[(string)($row['verify'] ?? '')] ?? (string)($row['verify'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td>
                                <td data-label="Tên máy"><?= htmlspecialchars($row['device_name'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
                                <td data-label="Thao tác">
                                <div class="att-actions">
                                    <button type="button"
                                            class="att-btn-icon att-edit-btn"
                                            title="Sửa"
                                            data-att-edit-pin="<?= htmlspecialchars($row['pin'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
                                            data-att-edit-name="<?= htmlspecialchars($row['name'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
                                            data-att-edit-datetime="<?= htmlspecialchars($row['datetime'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
                                            data-att-edit-date="<?= htmlspecialchars($row['date'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
                                            data-att-edit-time="<?= htmlspecialchars($row['time'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
                                    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                        <path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/>
                                    </svg>
                                    </button>
                                </div>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                    <?php else: ?>
                        <tr>
                            <td colspan="8" style="text-align:center;opacity:.7;">Chưa có dữ liệu hiển thị</td>
                        </tr>
                    <?php endif; ?>
                </tbody>
            </table>
        </div>
        <style>
          /* Đổi màu chữ cho dòng có Kiểu xác thực = '-----' */
          .attendance-panel .att-row-dash { background-color: transparent !important; color: #79DBC8 !important; }
          .attendance-panel .att-row-dash td { color: #79DBC8 !important; }
        </style>
        <script>
          // Hàm kiểm tra và đổi màu dòng có Kiểu xác thực = '-----'
          function highlightAttendanceRowsDash(){
            var table = document.getElementById('attendance-table');
            if (!table) return;
            var rows = table.querySelectorAll('tbody tr');
            rows.forEach(function(tr){
              var cell = tr.querySelector('td[data-label="Kiểu xác thực"]');
              if (!cell) return;
              var txt = (cell.textContent || '').trim();
              if (txt === '-----') tr.classList.add('att-row-dash');
              else tr.classList.remove('att-row-dash');
            });
          }
          document.addEventListener('DOMContentLoaded', highlightAttendanceRowsDash);
        </script>

        <?php if ($totalPages > 1): ?>
        <div class="pagination-wrapper">
            <div class="pagination">
                <?php if ($currentPage > 1): ?>
                    <a href="index.php?attendance&page=<?= $currentPage - 1 ?>&page_size=<?= (int)$pageSize ?>" class="pagination-btn pagination-prev">
                        <svg viewBox="0 0 24 24" style="width: 16px; height: 16px; fill: currentColor;">
                            <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
                        </svg>
                        <?= htmlspecialchars($messages['pagination_previous'] ?? 'Trước', ENT_QUOTES, 'UTF-8') ?>
                    </a>
                <?php endif; ?>

                <div class="pagination-numbers">
                    <?php
                    $startPage = max(1, $currentPage - 2);
                    $endPage = min($totalPages, $currentPage + 2);
                    if ($startPage > 1): ?>
                        <a href="index.php?attendance&page=1&page_size=<?= (int)$pageSize ?>" class="pagination-number">1</a>
                        <?php if ($startPage > 2): ?>
                            <span class="pagination-ellipsis">...</span>
                        <?php endif; ?>
                    <?php endif; ?>

                    <?php for ($i = $startPage; $i <= $endPage; $i++): ?>
                        <a href="index.php?attendance&page=<?= $i ?>&page_size=<?= (int)$pageSize ?>" class="pagination-number <?php echo ($i === $currentPage ? 'active' : ''); ?>"><?= $i ?></a>
                    <?php endfor; ?>

                    <?php if ($endPage < $totalPages): ?>
                        <?php if ($endPage < $totalPages - 1): ?>
                            <span class="pagination-ellipsis">...</span>
                        <?php endif; ?>
                        <a href="index.php?attendance&page=<?= $totalPages ?>" class="pagination-number"><?= $totalPages ?></a>
                    <?php endif; ?>
                </div>

                <?php if ($currentPage < $totalPages): ?>
                    <a href="index.php?attendance&page=<?= $currentPage + 1 ?>&page_size=<?= (int)$pageSize ?>" class="pagination-btn pagination-next">
                        <?= htmlspecialchars($messages['pagination_next'] ?? 'Sau', ENT_QUOTES, 'UTF-8') ?>
                        <svg viewBox="0 0 24 24" style="width: 16px; height: 16px; fill: currentColor;">
                            <path d="M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z"/>
                        </svg>
                    </a>
                <?php endif; ?>
            </div>
        </div>
        <?php endif; ?>

        <!-- Page size selector (custom select like theme) -->
        <div class="pagination-extras" style="margin: 8px 0 20px; display:flex; justify-content:center;">
            <form id="attendance-page-size-form" method="get" action="index.php">
                <input type="hidden" name="attendance" value="" />
                <input type="hidden" name="page" value="1" />
                <div class="page-size-pill" style="display:inline-flex; align-items:center; gap:8px; padding:6px 12px; background: rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.08); border-radius:10px; white-space:nowrap;">
                    <span style="font-size:12px; color:#9ec9c1; display:inline-block;">Hiển thị:</span>
                    <div class="custom-select" id="attendance-pagesize-custom-select" style="display:inline-block;">
                        <button type="button" class="custom-select-toggle" style="min-width:54px; height:28px; line-height:28px; padding:0 28px 0 12px;" data-placeholder="<?= (int)$pageSize ?>" aria-label="Chọn số dòng mỗi trang">
                            <?= (int)$pageSize ?>
                        </button>
                        <div class="custom-select-menu" role="listbox">
                            <?php foreach ($allowedPageSizes as $opt): ?>
                                <div class="custom-select-option<?= ($opt === $pageSize ? ' is-selected' : '') ?>" role="option" data-value="<?= (int)$opt ?>"><?= (int)$opt ?></div>
                            <?php endforeach; ?>
                        </div>
                        <input type="hidden" name="page_size" id="attendance-page-size" value="<?= (int)$pageSize ?>" />
                    </div>
                    <span style="font-size:12px; color:#9ec9c1; display:inline-block;">dòng/trang</span>
                </div>
            </form>
        </div>
        <script>
        (function(){
            var custom = document.getElementById('attendance-pagesize-custom-select');
            if (!custom) return;
            var toggle = custom.querySelector('.custom-select-toggle');
            var menu = custom.querySelector('.custom-select-menu');
            var hidden = document.getElementById('attendance-page-size');
            var form = document.getElementById('attendance-page-size-form');
            if (!toggle || !menu || !hidden || !form) return;

            var anchorParent = null, anchorNext = null, isPortaled = false;
            var closeOnScroll = null, closeOnResize = null;

            function mountMenu(){
                if (isPortaled) return;
                anchorParent = menu.parentNode;
                anchorNext = menu.nextSibling;
                document.body.appendChild(menu);
                isPortaled = true;
            }
            function unmountMenu(){
                if (!isPortaled) return;
                if (anchorParent){
                    if (anchorNext){ anchorParent.insertBefore(menu, anchorNext); }
                    else { anchorParent.appendChild(menu); }
                }
                isPortaled = false;
            }
            function positionMenu(){
                var r = toggle.getBoundingClientRect();
                var pad = 12;
                var OFFSET_X = 2; // fine-tune horizontally
                var OFFSET_Y = 2; // fine-tune vertically
                var scrollX = (window.pageXOffset || document.documentElement.scrollLeft);
                var scrollY = (window.pageYOffset || document.documentElement.scrollTop);
                var left = Math.floor(scrollX + r.left + OFFSET_X);
                var width = Math.round(r.width);
                if (left < pad) left = pad;
                var viewportWidth = (window.innerWidth || document.documentElement.clientWidth);
                if (left + width + pad > (scrollX + viewportWidth)){
                    left = Math.max(pad, (scrollX + viewportWidth) - width - pad);
                }
                var spacing = 2 + OFFSET_Y;
                var top = Math.floor(scrollY + r.top + toggle.offsetHeight + spacing);
                // If not enough space below, open upward
                var desiredHeight = 220;
                var viewportBottom = scrollY + (window.innerHeight || document.documentElement.clientHeight) - 8;
                if (top + desiredHeight > viewportBottom){
                    top = Math.max(8, Math.floor(scrollY + r.top - desiredHeight - spacing));
                }
                menu.style.position = 'absolute';
                menu.style.left = left + 'px';
                menu.style.top  = top + 'px';
                menu.style.width = width + 'px';
                menu.style.minWidth = width + 'px';
                menu.style.maxWidth = width + 'px';
                menu.style.maxHeight = '260px';
                menu.style.overflowY = 'auto';
                menu.style.zIndex = '2147483000';
                menu.style.display = 'block';
            }
            function clearStyles(){
                menu.style.position = '';
                menu.style.left = '';
                menu.style.top = '';
                menu.style.width = '';
                menu.style.minWidth = '';
                menu.style.maxWidth = '';
                menu.style.maxHeight = '';
                menu.style.overflowY = '';
                menu.style.zIndex = '';
                menu.style.display = '';
            }
            function closeAll(){
                custom.classList.remove('open');
                clearStyles();
                unmountMenu();
                if (closeOnScroll){ window.removeEventListener('scroll', closeOnScroll, true); closeOnScroll = null; }
                if (closeOnResize){ window.removeEventListener('resize', closeOnResize); closeOnResize = null; }
            }

            // Prevent page scrolling when interacting with menu
            menu.addEventListener('wheel', function(e){ e.stopPropagation(); }, { passive: true });
            menu.addEventListener('touchmove', function(e){ e.stopPropagation(); }, { passive: true });
            menu.addEventListener('mousedown', function(e){ e.stopPropagation(); });

            document.addEventListener('click', function(e){ if (!custom.contains(e.target) && !menu.contains(e.target)) closeAll(); });
            toggle.addEventListener('click', function(e){
                e.stopPropagation();
                var willOpen = !custom.classList.contains('open');
                custom.classList.toggle('open');
                if (willOpen){
                    mountMenu();
                    positionMenu();
                    closeOnScroll = function(ev){ if (menu.contains(ev.target)) return; closeAll(); };
                    closeOnResize = function(){ closeAll(); };
                    window.addEventListener('scroll', closeOnScroll, true);
                    window.addEventListener('resize', closeOnResize);
                } else {
                    closeAll();
                }
            });

            menu.addEventListener('click', function(e){
                var opt = e.target.closest('.custom-select-option');
                if (!opt) return;
                var value = opt.getAttribute('data-value');
                hidden.value = value;
                toggle.textContent = value;
                menu.querySelectorAll('.custom-select-option').forEach(function(el){ el.classList.toggle('is-selected', el===opt); });
                closeAll();
                form.submit();
            });
        })();
        </script>
    </div>
</section>

<!-- Time Picker Styles (scoped) --><style>.luna-time-picker{position:fixed;width:220px;height:240px;background:rgba(12,38,43,.98);border:1px solid rgba(122,220,201,.35);border-radius:12px;box-shadow:0 18px 38px rgba(3,15,21,.75);z-index:2147483000;display:none;padding:12px;box-sizing:border-box}.luna-time-picker.show{display:block}.luna-time-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid rgba(122,220,201,.2)}.luna-time-display{font-size:13px;font-weight:600;color:#e8fbf6;letter-spacing:1px}.luna-time-actions{display:flex;gap:6px;justify-content:flex-end}.luna-time-actions button{background:transparent;border:1px solid rgba(122,220,201,.28);color:#bfeee3;border-radius:6px;padding:4px 8px;cursor:pointer; font-size:9px;}.luna-time-actions button:hover{background:rgba(122,220,201,.12);color:#7adcc9}.luna-time-columns{display:grid;grid-template-columns:1fr 20px 1fr 20px 1fr;gap:6px;height:150px}.time-col{overflow-y:auto;border:1px solid rgba(122,220,201,.18);border-radius:8px;padding:4px}.time-sep{display:grid;place-items:center;color:#9ec9c1;font-weight:600}.time-item{text-align:center;padding:3px 0;color:#e8fbf6;border-radius:6px;cursor:pointer;font-size:13px}.time-item:hover{background:rgba(122,220,201,.12)}.time-item.selected{background:#7adcc9;color:#0b2228;font-weight:600}.time-labels{display:grid;grid-template-columns:1fr 20px 1fr 20px 1fr;gap:6px;margin-top:6px;color:#9ec9c1;font-size:11px;font-weight:600;text-align:center}.time-labels .spacer{width:20px}.luna-time-actions .ok-btn-header{background: #7adcc9;color:#052729 ;}.luna-time-actions .ok-btn-header:hover{color:#052729;background: rgba(121, 219, 200, 0.58) ;}</style>
<style>
#attendance-modal #att-time{font-size:16px!important;font-family: monospace;line-height:1.2;color:#e8fbf6;font-weight:400;-webkit-text-fill-color:#e8fbf6;border:0!important;background:transparent!important}
#attendance-modal #att-time::placeholder{color:#9ec9c1;opacity:.8}
#attendance-edit-modal #att-edit-time{font-size:16px!important;font-family: monospace;line-height:1.2;color:#e8fbf6;font-weight:400;-webkit-text-fill-color:#e8fbf6;border:0!important;background:transparent!important}
#attendance-edit-modal #att-edit-time::placeholder{color:#9ec9c1;opacity:.8}
#attendance-modal #att-time-wrap, #attendance-edit-modal #att-edit-time-wrap{border:1px solid rgba(122,220,201,.28)!important;border-radius:16px!important;background:rgba(11, 31, 34, 0.65)!important}
#attendance-modal #att-time-wrap:focus-within, #attendance-edit-modal #att-edit-time-wrap:focus-within{border-color:rgba(122,220,201,.45)!important;box-shadow:0 0 0 2px rgba(122,220,201,.08)!important}
</style>
<!-- Attendance Create Modal -->
<div id="attendance-modal" class="modal-backdrop" aria-hidden="true">
    <div class="modal">
        <div class="modal-header">
            <h4>THÊM DÒNG CHẤM CÔNG</h4>
            <button type="button" class="modal-close" data-modal-close>&times;</button>
        </div>
        <div class="modal-body">
            <form id="attendance-form" method="POST" class="form-grid grid-2" novalidate>
                <input type="hidden" name="attendance_create" value="1">

                <div class="form-row">
                    <label for="att-pin">Mã nhân viên</label>
                    <div class="input-wrap">
                        <span class="icon">
                            <svg viewBox="0 0 24 24"><path d="M4 4h16v4H4zM4 12h10v8H4zM16 12h4v8h-4z"/></svg>
                        </span>
                        <input type="text" id="att-pin" name="att_pin" placeholder="VD: 00092" required>
                    </div>
                </div>

                <div class="form-row">
                    <label for="att-name">Họ tên</label>
                    <div class="input-wrap">
                        <span class="icon">
                            <svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
                        </span>
                        <input type="text" id="att-name" name="att_name" placeholder="Nhập họ tên">
                    </div>
                </div>

                <div class="form-row">
                    <label for="att-date">Ngày</label>
                    <div class="input-wrap">
                        <span class="icon date-icon" data-target="att-date">
                            <svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
                        </span>
                        <input type="date" id="att-date" name="att_date" required autocomplete="off" style="font-size:16px;">
                    </div>
                </div>

                <div class="form-row">
                    <label for="att-time">Giờ</label>
                    <div class="input-wrap" id="att-time-wrap">
                        <span class="icon time-icon" data-target="att-time">
                            <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg>
                        </span>
                        <input type="text" id="att-time" name="att_time" placeholder="hh:mm:ss" autocomplete="off" inputmode="numeric" pattern="^\d{2}:\d{2}:\d{2}$" style="font-size:16px;" readonly onclick="window.openTimePickerAtt && window.openTimePickerAtt(); return false;" onfocus="window.openTimePickerAtt && window.openTimePickerAtt(); return false;">
                    </div>
                </div>

                <div class="form-actions full">
                    <button type="submit" class="btn btn-primary">Lưu</button>
                    <button type="button" class="btn btn-secondary" data-modal-close>Hủy</button>
                </div>
            </form>
        </div>
    </div>
</div>

<!-- Attendance Edit Modal -->
<div id="attendance-edit-modal" class="modal-backdrop" aria-hidden="true">
    <div class="modal">
        <div class="modal-header">
            <h4>CHỈNH SỬA THỜI GIAN</h4>
            <button type="button" class="modal-close" data-modal-close>&times;</button>
        </div>
        <div class="modal-body">
            <form id="attendance-edit-form" method="POST" class="form-grid grid-2" novalidate>
                <input type="hidden" name="attendance_edit" value="1">
                <input type="hidden" id="att-edit-pin" name="att_edit_pin">
                <input type="hidden" id="att-edit-old-datetime" name="att_edit_old_datetime">

                <div class="form-row">
                    <label for="att-edit-pin-display">Mã nhân viên</label>
                    <div class="input-wrap">
                        <span class="icon">
                            <svg viewBox="0 0 24 24"><path d="M4 4h16v4H4zM4 12h10v8H4zM16 12h4v8h-4z"/></svg>
                        </span>
                        <input type="text" id="att-edit-pin-display" placeholder="Mã nhân viên" disabled style="background:rgba(255,255,255,0.05); cursor:not-allowed;">
                    </div>
                </div>

                <div class="form-row">
                    <label for="att-edit-name-display">Họ tên</label>
                    <div class="input-wrap">
                        <span class="icon">
                            <svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
                        </span>
                        <input type="text" id="att-edit-name-display" placeholder="Họ tên" disabled style="background:rgba(255,255,255,0.05); cursor:not-allowed;">
                    </div>
                </div>

                <div class="form-row">
                    <label for="att-edit-date">Ngày</label>
                    <div class="input-wrap">
                        <span class="icon date-icon is-disabled" data-target="att-edit-date" disabled style="pointer-events:none; opacity:.6;">
                            <svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
                        </span>
                        <input type="date" id="att-edit-date" name="att_edit_date" autocomplete="off" style="font-size:16px;background: rgba(255, 255, 255, 0.05);cursor:not-allowed;" disabled readonly data-no-picker="1">
                    </div>
                </div>

                <div class="form-row">
                    <label for="att-edit-time">Giờ</label>
                    <div class="input-wrap" id="att-edit-time-wrap">
                        <span class="icon time-icon" data-target="att-edit-time">
                            <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg>
                        </span>
                        <input type="text" id="att-edit-time" name="att_edit_time" placeholder="hh:mm:ss" autocomplete="off" inputmode="numeric" pattern="^\d{2}:\d{2}:\d{2}$" style="font-size:16px;" readonly onclick="window.openTimePickerAttEdit && window.openTimePickerAttEdit(); return false;" onfocus="window.openTimePickerAttEdit && window.openTimePickerAttEdit(); return false;">
                    </div>
                </div>

                <div class="form-actions full">
                    <button type="submit" class="btn btn-primary">Lưu</button>
                    <button type="button" class="btn btn-secondary" data-modal-close>Hủy</button>
                </div>
            </form>
        </div>
    </div>
</div>

<script>
// Attendance modal open/close (match behavior of Incoming modal)
(function(){
  var openBtn = document.getElementById('attendance-open-create');
  var modal = document.getElementById('attendance-modal');
  if (!openBtn || !modal) return;
  var closeButtons = modal.querySelectorAll('[data-modal-close]');
  function open(){
    modal.classList.add('show');
    modal.removeAttribute('aria-hidden');
    modal.setAttribute('aria-hidden','false');
    modal.style.display = 'flex';
    modal.style.zIndex = '100000';
    setTimeout(function(){
      var first = document.getElementById('att-pin');
      if (first) { try { first.focus(); } catch(_) {} }
    }, 50);
  }
  function close(){
    var active = document.activeElement;
    if (active && modal.contains(active)) { try { active.blur(); } catch(_) {} }
    modal.classList.remove('show');
    modal.setAttribute('aria-hidden','true');
    modal.style.display = 'none';
  }
  openBtn.addEventListener('click', open);
  closeButtons.forEach(function(btn){ btn.addEventListener('click', close); });
  modal.addEventListener('click', function(e){ if (e.target === modal) close(); });
  document.addEventListener('keydown', function(e){ if (e.key === 'Escape') close(); });
})();

// Attendance edit modal open/close
(function(){
  var editModal = document.getElementById('attendance-edit-modal');
  if (!editModal) return;
  var closeButtons = editModal.querySelectorAll('[data-modal-close]');
  
  function open(pin, name, datetime, date, time){
    document.getElementById('att-edit-pin').value = pin;
    document.getElementById('att-edit-old-datetime').value = datetime;
    document.getElementById('att-edit-pin-display').value = pin;
    document.getElementById('att-edit-name-display').value = name;
    
    function toIsoDate(str){
      if (!str) return '';
      str = String(str).trim();
      // Already ISO yyyy-mm-dd
      var m = str.match(/^(\d{4})-(\d{2})-(\d{2})/);
      if (m) return m[1] + '-' + m[2] + '-' + m[3];
      // dd/mm/yyyy or dd-mm-yyyy
      m = str.match(/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})$/);
      if (m){
        var d = ('0'+m[1]).slice(-2), mo = ('0'+m[2]).slice(-2), y = m[3];
        return y + '-' + mo + '-' + d;
      }
      // Try parsing generic date
      try {
        var d2 = new Date(str);
        if (!isNaN(d2.getTime())){
          var mm = ('0'+(d2.getMonth()+1)).slice(-2);
          var dd = ('0'+d2.getDate()).slice(-2);
          return d2.getFullYear() + '-' + mm + '-' + dd;
        }
      } catch(_) {}
      return '';
    }

    var isoDate = toIsoDate(date) || toIsoDate(datetime);
    var dateInputEl = document.getElementById('att-edit-date');
    if (dateInputEl) dateInputEl.value = isoDate;

    // Time: prefer explicit time, fallback to datetime
    var timeVal = '';
    if (time && /^\d{2}:\d{2}(:\d{2})?$/.test(time.trim())) {
      timeVal = time.trim().length===5 ? time.trim()+':00' : time.trim();
    } else if (datetime){
      var t = String(datetime).split(' ')[1] || String(datetime).split('T')[1] || '';
      timeVal = t ? t.substring(0,8) : '';
    }
    var timeInputEl = document.getElementById('att-edit-time');
    if (timeInputEl) timeInputEl.value = timeVal;
    
    editModal.classList.add('show');
    editModal.removeAttribute('aria-hidden');
    editModal.setAttribute('aria-hidden','false');
    editModal.style.display = 'flex';
    editModal.style.zIndex = '100000';
    setTimeout(function(){
      var el = document.getElementById('att-edit-time');
      if (el) { try { el.focus(); } catch(_) {} }
    }, 50);
  }
  
  function close(){
    var active = document.activeElement;
    if (active && editModal.contains(active)) { try { active.blur(); } catch(_) {} }
    editModal.classList.remove('show');
    editModal.setAttribute('aria-hidden','true');
    editModal.style.display = 'none';
  }
  
  closeButtons.forEach(function(btn){ btn.addEventListener('click', close); });
  editModal.addEventListener('click', function(e){ if (e.target === editModal) close(); });
  document.addEventListener('keydown', function(e){ if (e.key === 'Escape') close(); });
  
  // Expose globally
  window.openAttendanceEditModal = open;
  
  // Bind edit buttons
  document.addEventListener('click', function(e){
    var btn = e.target.closest('.att-edit-btn');
    if (!btn) return;
    var pin = btn.getAttribute('data-att-edit-pin') || '';
    var name = btn.getAttribute('data-att-edit-name') || '';
    var datetime = btn.getAttribute('data-att-edit-datetime') || '';
    var date = btn.getAttribute('data-att-edit-date') || '';
    var time = btn.getAttribute('data-att-edit-time') || '';
    open(pin, name, datetime, date, time);
  });
})();
</script>

<script>
// Custom Date Picker for Attendance modal (reuse same component as Incoming)
document.addEventListener('DOMContentLoaded', function(){
  var attendanceModal = document.getElementById('attendance-modal');
  if (!attendanceModal) return;
  
  setupDatePickerForModal(attendanceModal);
});

// Custom Date Picker for Attendance Edit modal
document.addEventListener('DOMContentLoaded', function(){
  var editModal = document.getElementById('attendance-edit-modal');
  if (!editModal) return;
  
  setupDatePickerForModal(editModal);
});

// Custom Date Picker for Fetch Range modal
document.addEventListener('DOMContentLoaded', function(){
  var fetchModal = document.getElementById('attendance-fetch-modal');
  if (!fetchModal) return;

  setupDatePickerForModal(fetchModal);
});

function setupDatePickerForModal(attendanceModal){
  if (!attendanceModal) return;

  var monthNames = ['Tháng Một','Tháng Hai','Tháng Ba','Tháng Tư','Tháng Năm','Tháng Sáu','Tháng Bảy','Tháng Tám','Tháng Chín','Tháng Mười','Tháng Mười Một','Tháng Mười Hai'];
  var dayNames = ['CN','T2','T3','T4','T5','T6','T7'];

  var pickerEl = null, currentInput = null, currentDate = new Date();
  var wheelHandler = null, clickOutsideHandler = null, scrollHandler = null, resizeHandler = null, rafId = null;

  function createPicker(){
    var el = document.createElement('div');
    el.className = 'luna-date-picker';
    el.innerHTML = '\
      <div class="luna-date-picker-header">\
        <div class="luna-date-picker-month-year"></div>\
        <div class="luna-date-picker-nav">\
          <button type="button" class="luna-date-picker-nav-btn prev-year" title="Năm trước"><svg viewBox="0 0 24 24"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></svg></button>\
          <button type="button" class="luna-date-picker-nav-btn prev-month" title="Tháng trước"><svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg></button>\
          <button type="button" class="luna-date-picker-nav-btn next-month" title="Tháng sau"><svg viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>\
          <button type="button" class="luna-date-picker-nav-btn next-year" title="Năm sau"><svg viewBox="0 0 24 24"><polyline points="13 17 18 12 13 7"/><polyline points="6 17 11 12 6 7"/></svg></button>\
        </div>\
      </div>\
      <div class="luna-date-picker-weekdays"></div>\
      <div class="luna-date-picker-days"></div>\
      <div class="luna-date-picker-footer">\
        <button type="button" class="luna-date-picker-footer-btn clear-btn">Xóa</button>\
        <button type="button" class="luna-date-picker-footer-btn today-btn">Hôm nay</button>\
      </div>';
    document.body.appendChild(el);
    return el;
  }

  function getPicker(){ if (!pickerEl) pickerEl = createPicker(); return pickerEl; }

  function updatePickerPosition(){
    if (!pickerEl || !currentInput || !pickerEl.classList.contains('show')) return;
    var rect = currentInput.getBoundingClientRect();
    var wrap = currentInput.closest('.input-wrap');
    var box = wrap ? wrap.getBoundingClientRect() : rect;
    var left = box.left; var top = box.bottom + 6;
    var pickerW = 230, pickerH = 280;
    if (left + pickerW > window.innerWidth) left = window.innerWidth - pickerW - 10;
    if (left < 10) left = 10;
    if (top + pickerH > window.innerHeight) top = box.top - pickerH - 6;
    if (top < 10) top = 10;
    pickerEl.style.left = left + 'px';
    pickerEl.style.top = top + 'px';
  }
  function updatePickerPositionRAF(){ if (rafId) cancelAnimationFrame(rafId); rafId = requestAnimationFrame(function(){ updatePickerPosition(); rafId = null; }); }

  function render(){
    var el = getPicker(); if (!el || !currentInput) return;
    var y = currentDate.getFullYear(), m = currentDate.getMonth();
    el.querySelector('.luna-date-picker-month-year').innerHTML = '<span class="month">' + monthNames[m] + '</span><span class="year">' + y + '</span>';
    var weekdays = el.querySelector('.luna-date-picker-weekdays'); weekdays.innerHTML='';
    dayNames.forEach(function(d){ var div=document.createElement('div'); div.className='luna-date-picker-weekday'; div.textContent=d; weekdays.appendChild(div); });
    var days = el.querySelector('.luna-date-picker-days'); days.innerHTML='';
    var first = new Date(y, m, 1), start = new Date(first); start.setDate(start.getDate() - start.getDay());
    var lastSelected=null; if (currentInput.value){ var p=currentInput.value.split('-'); if(p.length===3){ lastSelected=new Date(+p[0], +p[1]-1, +p[2]); lastSelected.setHours(0,0,0,0);} }
    var today=new Date(); today.setHours(0,0,0,0);
    for (var i=0;i<42;i++){
      var d=new Date(start); d.setDate(start.getDate()+i);
      var btn=document.createElement('button'); btn.type='button'; btn.className='luna-date-picker-day'; btn.textContent=d.getDate();
      if (d.getMonth()!==m) btn.classList.add('other-month');
      if (d.getTime()===today.getTime()) btn.classList.add('today');
      if (lastSelected && d.getTime()===lastSelected.getTime()) btn.classList.add('selected');
      (function(date){ btn.addEventListener('click', function(){ select(date); }); })(new Date(d));
      days.appendChild(btn);
    }
  }

  function select(date){ if(!currentInput) return; var y=date.getFullYear(), mo=('0'+(date.getMonth()+1)).slice(-2), da=('0'+date.getDate()).slice(-2); currentInput.value = y+'-'+mo+'-'+da; currentInput.dispatchEvent(new Event('change',{bubbles:true})); hide(); }

  function show(input){ if(!input||input.type!=='date' || input.disabled) return; currentInput=input; if(input.value){ var p=input.value.split('-'); if(p.length===3){ currentDate=new Date(+p[0],+p[1]-1,+p[2]); } else { currentDate=new Date(); } } else { currentDate=new Date(); }
    var el=getPicker(); render(); updatePickerPosition(); el.classList.add('show');
    // events
    // cleanup old
    if (wheelHandler) { el.removeEventListener('wheel', wheelHandler); }
    if (clickOutsideHandler) { document.removeEventListener('click', clickOutsideHandler); }
    if (scrollHandler) { window.removeEventListener('scroll', scrollHandler, true); }
    if (resizeHandler) { window.removeEventListener('resize', resizeHandler); }
    // nav
    el.querySelector('.prev-year').onclick=function(){ currentDate.setMonth(currentDate.getMonth()-12); render(); };
    el.querySelector('.prev-month').onclick=function(){ currentDate.setMonth(currentDate.getMonth()-1); render(); };
    el.querySelector('.next-month').onclick=function(){ currentDate.setMonth(currentDate.getMonth()+1); render(); };
    el.querySelector('.next-year').onclick=function(){ currentDate.setMonth(currentDate.getMonth()+12); render(); };
    el.querySelector('.clear-btn').onclick=function(){ if(currentInput){ currentInput.value=''; hide(); } };
    el.querySelector('.today-btn').onclick=function(){ select(new Date()); };
    // wheel throttle
    var lastWheel=0; wheelHandler=function(e){ if(!el.classList.contains('show')) return; e.preventDefault(); var now=Date.now(); if(now-lastWheel<150) return; lastWheel=now; var delta=e.deltaY||e.detail||-e.wheelDelta; if(delta>0) currentDate.setMonth(currentDate.getMonth()+1); else if(delta<0) currentDate.setMonth(currentDate.getMonth()-1); render(); };
    el.addEventListener('wheel', wheelHandler, {passive:false});
    scrollHandler=function(){ if(el.classList.contains('show')) updatePickerPositionRAF(); };
    resizeHandler=function(){ if(el.classList.contains('show')) updatePickerPositionRAF(); };
    window.addEventListener('scroll', scrollHandler, true);
    window.addEventListener('resize', resizeHandler);
    clickOutsideHandler=function(e){ if(!el.classList.contains('show')) return; var inside=el.contains(e.target); var onInput=(e.target===currentInput||currentInput.contains(e.target)); var onIcon=!!e.target.closest('.date-icon'); if(!inside && !onInput && !onIcon){ hide(); } };
    document.addEventListener('click', clickOutsideHandler);
  }
  function hide(){ if(!pickerEl) return; pickerEl.classList.remove('show'); if (wheelHandler){ pickerEl.removeEventListener('wheel', wheelHandler); wheelHandler=null; } if(clickOutsideHandler){ document.removeEventListener('click', clickOutsideHandler); clickOutsideHandler=null; } if(scrollHandler){ window.removeEventListener('scroll', scrollHandler, true); scrollHandler=null; } if(resizeHandler){ window.removeEventListener('resize', resizeHandler); resizeHandler=null; } currentInput=null; }

  // Bind icon and input - att-date
  var icon = attendanceModal.querySelector('.date-icon[data-target="att-date"]');
  if (icon){ icon.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); var input=document.getElementById('att-date'); if(input) show(input); }); }
  var input = document.getElementById('att-date');
  if (input){ input.addEventListener('click', function(e){ e.preventDefault(); show(input); }); input.addEventListener('focus', function(e){ e.preventDefault(); show(input); }); }
  
  // Bind icon and input - att-edit-date
  var iconEdit = attendanceModal.querySelector('.date-icon[data-target="att-edit-date"]');
  if (iconEdit){ iconEdit.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); var input=document.getElementById('att-edit-date'); if(input) show(input); }); }
  var inputEdit = document.getElementById('att-edit-date');
  if (inputEdit){ inputEdit.addEventListener('click', function(e){ e.preventDefault(); show(inputEdit); }); inputEdit.addEventListener('focus', function(e){ e.preventDefault(); show(inputEdit); }); }

  // Bind icon and input - fetch range: att-fetch-from
  var iconFrom = attendanceModal.querySelector('.date-icon[data-target="att-fetch-from"]');
  if (iconFrom){ iconFrom.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); var input=document.getElementById('att-fetch-from'); if(input) show(input); }); }
  var inputFrom = document.getElementById('att-fetch-from');
  if (inputFrom){ inputFrom.addEventListener('click', function(e){ e.preventDefault(); show(inputFrom); }); inputFrom.addEventListener('focus', function(e){ e.preventDefault(); show(inputFrom); }); }

  // Bind icon and input - fetch range: att-fetch-to
  var iconTo = attendanceModal.querySelector('.date-icon[data-target="att-fetch-to"]');
  if (iconTo){ iconTo.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); var input=document.getElementById('att-fetch-to'); if(input) show(input); }); }
  var inputTo = document.getElementById('att-fetch-to');
  if (inputTo){ inputTo.addEventListener('click', function(e){ e.preventDefault(); show(inputTo); }); inputTo.addEventListener('focus', function(e){ e.preventDefault(); show(inputTo); }); }
}
</script>

<script>
// Custom Time Picker for Attendance modal
document.addEventListener('DOMContentLoaded', function(){
  var attendanceModal = document.getElementById('attendance-modal');
  if (!attendanceModal) return;

  var picker = null, currentInput = null;
  var selectedH = 0, selectedM = 0, selectedS = 0;
  var closeHandlers = { scroll:null, resize:null, outside:null };
  var suppressClose = false;

  function createTimePicker(){
    var el = document.createElement('div');
    el.className = 'luna-time-picker';
    el.innerHTML = '\
      <div class="luna-time-header">\
        <div class="luna-time-display" id="tp-display">00:00:00</div>\
        <div class="luna-time-actions">\
          <button type="button" class="now-btn">Bây giờ</button>\
          <button type="button" class="clear-btn">Xóa</button>\
          <button type="button" class="ok-btn-header">OK</button>\
        </div>\
      </div>\
      <div class="luna-time-columns">\
        <div class="time-col hours"></div>\
        <div class="time-sep">:</div>\
        <div class="time-col minutes"></div>\
        <div class="time-sep">:</div>\
        <div class="time-col seconds"></div>\
      </div>\
      <div class="time-labels">\
        <div class="lbl">GIỜ</div><div class="spacer"></div><div class="lbl">PHÚT</div><div class="spacer"></div><div class="lbl">GIÂY</div>\
      </div>';
    document.body.appendChild(el);
    return el;
  }

  function getPicker(){ if(!picker) picker = createTimePicker(); return picker; }

  function updateDisplay(){
    var d = document.getElementById('tp-display');
    if (d) d.textContent = String(selectedH).padStart(2,'0') + ':' + String(selectedM).padStart(2,'0') + ':' + String(selectedS).padStart(2,'0');
  }

  function renderColumns(){
    var el = getPicker();
    var hCol = el.querySelector('.hours');
    var mCol = el.querySelector('.minutes');
    var sCol = el.querySelector('.seconds');
    hCol.innerHTML = ''; mCol.innerHTML=''; sCol.innerHTML='';
    for (var h=0; h<24; h++){
      var b=document.createElement('div'); b.className='time-item'+(h===selectedH?' selected':''); b.textContent=String(h).padStart(2,'0'); b.dataset.h=h;
      b.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); selectedH = parseInt(e.target.dataset.h,10); renderColumns(); updateDisplay(); });
      hCol.appendChild(b);
    }
    for (var m=0; m<60; m+=1){
      var c=document.createElement('div'); c.className='time-item'+(m===selectedM?' selected':''); c.textContent=String(m).padStart(2,'0'); c.dataset.m=m;
      c.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); selectedM = parseInt(e.target.dataset.m,10); renderColumns(); updateDisplay(); });
      mCol.appendChild(c);
    }
    for (var s=0; s<60; s+=1){
      var d=document.createElement('div'); d.className='time-item'+(s===selectedS?' selected':''); d.textContent=String(s).padStart(2,'0'); d.dataset.s=s;
      d.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); selectedS = parseInt(e.target.dataset.s,10); renderColumns(); updateDisplay(); });
      sCol.appendChild(d);
    }
    updateDisplay();
  }

  function position(){
    var el = getPicker(); if(!currentInput) return;
    var rect = currentInput.getBoundingClientRect();
    var wrap = currentInput.closest('.input-wrap');
    var box = wrap ? wrap.getBoundingClientRect() : rect;
    var left = box.left; var top = box.bottom + 6;
    var w = 220, h = 240;
    if (left + w > window.innerWidth) left = window.innerWidth - w - 10;
    if (left < 10) left = 10;
    if (top + h > window.innerHeight) top = box.top - h - 6;
    if (top < 10) top = 10;
    el.style.left = left + 'px'; el.style.top = top + 'px';
  }
  function show(input){
    currentInput = input;
    // Parse current value (HH:MM[:SS])
    var h=0,m=0,s=0;
    if (input && input.value){
      var parts = input.value.split(':');
      if (parts.length>=2){ h = parseInt(parts[0]||'0',10)||0; m = parseInt(parts[1]||'0',10)||0; }
      if (parts.length>=3){ s = parseInt(parts[2]||'0',10)||0; }
    } else {
      var now = new Date(); h = now.getHours(); m = now.getMinutes(); s = now.getSeconds();
    }
    selectedH = Math.max(0, Math.min(23, h));
    selectedM = Math.max(0, Math.min(59, m));
    selectedS = Math.max(0, Math.min(59, s));

    var el = getPicker(); renderColumns(); position(); el.classList.add('show');

    // Bind actions
    el.querySelector('.now-btn').onclick = function(){ var n=new Date(); var v = String(n.getHours()).padStart(2,'0') + ':' + String(n.getMinutes()).padStart(2,'0') + ':' + String(n.getSeconds()).padStart(2,'0'); if (currentInput){ currentInput.value = v; currentInput.dispatchEvent(new Event('change',{bubbles:true})); } hide(); };
    el.querySelector('.clear-btn').onclick = function(){ if(currentInput){ currentInput.value = ''; } hide(); };
    var okHeader = el.querySelector('.ok-btn-header');
    if (okHeader) {
      okHeader.onclick = function(){
        if (currentInput) {
          var v = String(selectedH).padStart(2,'0') + ':' + String(selectedM).padStart(2,'0') + ':' + String(selectedS).padStart(2,'0');
          currentInput.value = v;
          currentInput.dispatchEvent(new Event('change',{bubbles:true}));
        }
        hide();
      };
    }

    // Outside/scroll/resize close
    closeHandlers.outside = function(e){ if (!el.contains(e.target) && e.target !== currentInput && !e.target.closest('.time-icon')) hide(); };
    closeHandlers.scroll = function(){ if (el.classList.contains('show')) position(); };
    closeHandlers.resize = function(){ if (el.classList.contains('show')) position(); };
    document.addEventListener('click', closeHandlers.outside);
    window.addEventListener('scroll', closeHandlers.scroll, true);
    window.addEventListener('resize', closeHandlers.resize);
  }
  function hide(){ if(!picker) return; picker.classList.remove('show'); document.removeEventListener('click', closeHandlers.outside); window.removeEventListener('scroll', closeHandlers.scroll, true); window.removeEventListener('resize', closeHandlers.resize); currentInput=null; }

  // Bind icon & input
  var timeIcon = attendanceModal.querySelector('.time-icon[data-target="att-time"]');
  if (timeIcon){ timeIcon.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); var t=document.getElementById('att-time'); if(t) show(t); }); }
  var timeInput = document.getElementById('att-time');
  if (timeInput){ timeInput.addEventListener('click', function(e){ e.preventDefault(); show(timeInput); }); timeInput.addEventListener('focus', function(e){ e.preventDefault(); show(timeInput); }); }
  // Expose a global opener for inline handlers
  window.openTimePickerAtt = function(){ var t=document.getElementById('att-time'); if (t) show(t); };
  // Delegated fallback (in case elements are re-rendered)
  document.addEventListener('click', function(e){
    var icon = e.target.closest('.time-icon[data-target="att-time"]');
    if (icon){ e.preventDefault(); e.stopPropagation(); var t = document.getElementById('att-time'); if(t) show(t); return; }
    var inp = e.target.closest('#att-time');
    if (inp){ e.preventDefault(); show(inp); }
  }, true);
});

// Custom Time Picker for Attendance Edit modal
document.addEventListener('DOMContentLoaded', function(){
  var editModal = document.getElementById('attendance-edit-modal');
  if (!editModal) return;

  var picker = null, currentInput = null;
  var selectedH = 0, selectedM = 0, selectedS = 0;
  var closeHandlers = { scroll:null, resize:null, outside:null };

  function createTimePicker(){
    var el = document.createElement('div');
    el.className = 'luna-time-picker';
    el.id = 'luna-time-picker-edit';
    el.innerHTML = '\
      <div class="luna-time-header">\
        <div class="luna-time-display" id="tp-display-edit">00:00:00</div>\
        <div class="luna-time-actions">\
          <button type="button" class="now-btn">Bây giờ</button>\
          <button type="button" class="clear-btn">Xóa</button>\
          <button type="button" class="ok-btn-header">OK</button>\
        </div>\
      </div>\
      <div class="luna-time-columns">\
        <div class="time-col hours"></div>\
        <div class="time-sep">:</div>\
        <div class="time-col minutes"></div>\
        <div class="time-sep">:</div>\
        <div class="time-col seconds"></div>\
      </div>\
      <div class="time-labels">\
        <div class="lbl">GIỜ</div><div class="spacer"></div><div class="lbl">PHÚT</div><div class="spacer"></div><div class="lbl">GIÂY</div>\
      </div>';
    document.body.appendChild(el);
    return el;
  }

  function getPicker(){ if(!picker) picker = createTimePicker(); return picker; }

  function updateDisplay(){
    var d = document.getElementById('tp-display-edit');
    if (d) d.textContent = String(selectedH).padStart(2,'0') + ':' + String(selectedM).padStart(2,'0') + ':' + String(selectedS).padStart(2,'0');
  }

  function renderColumns(){
    var el = getPicker();
    var hCol = el.querySelector('.hours');
    var mCol = el.querySelector('.minutes');
    var sCol = el.querySelector('.seconds');
    hCol.innerHTML = ''; mCol.innerHTML=''; sCol.innerHTML='';
    for (var h=0; h<24; h++){
      var b=document.createElement('div'); b.className='time-item'+(h===selectedH?' selected':''); b.textContent=String(h).padStart(2,'0'); b.dataset.h=h;
      b.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); selectedH = parseInt(e.target.dataset.h,10); renderColumns(); updateDisplay(); });
      hCol.appendChild(b);
    }
    for (var m=0; m<60; m+=1){
      var c=document.createElement('div'); c.className='time-item'+(m===selectedM?' selected':''); c.textContent=String(m).padStart(2,'0'); c.dataset.m=m;
      c.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); selectedM = parseInt(e.target.dataset.m,10); renderColumns(); updateDisplay(); });
      mCol.appendChild(c);
    }
    for (var s=0; s<60; s+=1){
      var d=document.createElement('div'); d.className='time-item'+(s===selectedS?' selected':''); d.textContent=String(s).padStart(2,'0'); d.dataset.s=s;
      d.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); selectedS = parseInt(e.target.dataset.s,10); renderColumns(); updateDisplay(); });
      sCol.appendChild(d);
    }
    updateDisplay();
  }

  function position(){
    var el = getPicker(); if(!currentInput) return;
    var rect = currentInput.getBoundingClientRect();
    var wrap = currentInput.closest('.input-wrap');
    var box = wrap ? wrap.getBoundingClientRect() : rect;
    var left = box.left; var top = box.bottom + 6;
    var w = 220, h = 240;
    if (left + w > window.innerWidth) left = window.innerWidth - w - 10;
    if (left < 10) left = 10;
    if (top + h > window.innerHeight) top = box.top - h - 6;
    if (top < 10) top = 10;
    el.style.left = left + 'px'; el.style.top = top + 'px';
  }
  
  function show(input){
    currentInput = input;
    // Parse current value (HH:MM[:SS])
    var h=0,m=0,s=0;
    if (input && input.value){
      var parts = input.value.split(':');
      if (parts.length>=2){ h = parseInt(parts[0]||'0',10)||0; m = parseInt(parts[1]||'0',10)||0; }
      if (parts.length>=3){ s = parseInt(parts[2]||'0',10)||0; }
    } else {
      var now = new Date(); h = now.getHours(); m = now.getMinutes(); s = now.getSeconds();
    }
    selectedH = Math.max(0, Math.min(23, h));
    selectedM = Math.max(0, Math.min(59, m));
    selectedS = Math.max(0, Math.min(59, s));

    var el = getPicker(); renderColumns(); position(); el.classList.add('show');

    // Bind actions
    el.querySelector('.now-btn').onclick = function(){ var n=new Date(); var v = String(n.getHours()).padStart(2,'0') + ':' + String(n.getMinutes()).padStart(2,'0') + ':' + String(n.getSeconds()).padStart(2,'0'); if (currentInput){ currentInput.value = v; currentInput.dispatchEvent(new Event('change',{bubbles:true})); } hide(); };
    el.querySelector('.clear-btn').onclick = function(){ if(currentInput){ currentInput.value = ''; } hide(); };
    var okHeader = el.querySelector('.ok-btn-header');
    if (okHeader) {
      okHeader.onclick = function(){
        if (currentInput) {
          var v = String(selectedH).padStart(2,'0') + ':' + String(selectedM).padStart(2,'0') + ':' + String(selectedS).padStart(2,'0');
          currentInput.value = v;
          currentInput.dispatchEvent(new Event('change',{bubbles:true}));
        }
        hide();
      };
    }

    // Outside/scroll/resize close
    closeHandlers.outside = function(e){ if (!el.contains(e.target) && e.target !== currentInput && !e.target.closest('.time-icon')) hide(); };
    closeHandlers.scroll = function(){ if (el.classList.contains('show')) position(); };
    closeHandlers.resize = function(){ if (el.classList.contains('show')) position(); };
    document.addEventListener('click', closeHandlers.outside);
    window.addEventListener('scroll', closeHandlers.scroll, true);
    window.addEventListener('resize', closeHandlers.resize);
  }
  
  function hide(){ if(!picker) return; picker.classList.remove('show'); document.removeEventListener('click', closeHandlers.outside); window.removeEventListener('scroll', closeHandlers.scroll, true); window.removeEventListener('resize', closeHandlers.resize); currentInput=null; }

  // Bind icon & input
  var timeIcon = editModal.querySelector('.time-icon[data-target="att-edit-time"]');
  if (timeIcon){ timeIcon.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); var t=document.getElementById('att-edit-time'); if(t) show(t); }); }
  var timeInput = document.getElementById('att-edit-time');
  if (timeInput){ timeInput.addEventListener('click', function(e){ e.preventDefault(); show(timeInput); }); timeInput.addEventListener('focus', function(e){ e.preventDefault(); show(timeInput); }); }
  // Expose a global opener for inline handlers
  window.openTimePickerAttEdit = function(){ var t=document.getElementById('att-edit-time'); if (t) show(t); };
  // Delegated fallback
  document.addEventListener('click', function(e){
    var icon = e.target.closest('.time-icon[data-target="att-edit-time"]');
    if (icon){ e.preventDefault(); e.stopPropagation(); var t = document.getElementById('att-edit-time'); if(t) show(t); return; }
    var inp = e.target.closest('#att-edit-time');
    if (inp){ e.preventDefault(); show(inp); }
  }, true);
});
</script>

<script>
// Client-side validation for Attendance form - keep page and show toast on error
(function(){
  var form = document.getElementById('attendance-form');
  if (!form) return;
  form.addEventListener('submit', function(ev){
    var pinEl = document.getElementById('att-pin');
    var nameEl = document.getElementById('att-name');
    var dateEl = document.getElementById('att-date');
    var pin  = pinEl ? String(pinEl.value).trim()  : '';
    var name = nameEl ? String(nameEl.value).trim() : '';
    var date = dateEl ? String(dateEl.value).trim() : '';

    // reset decorations
    [pinEl,nameEl,dateEl].forEach(function(el){ if(el){ el.style.borderColor=''; el.style.boxShadow=''; }});

    var errs = [];
    if (!pin)  errs.push('* Mã nhân viên không được để trống.');
    if (!name) errs.push('* Họ tên không được để trống.');
    if (!date) errs.push('* Ngày không được để trống.');

    if (errs.length){
      ev.preventDefault();
      // highlight fields

      var title = (window.__MESSAGES__ && window.__MESSAGES__['error_notification_title']) ? window.__MESSAGES__['error_notification_title'] : 'THÔNG BÁO';
      var body  = errs.join('<br>');
      if (typeof window.showToast === 'function'){
        window.showToast('error', '<div class="error-header">'+title+'</div>'+body);
      } else {
        alert(errs.join('\n'));
      }
    }
  });
})();

// Client-side validation for Attendance Edit form
(function(){
  var form = document.getElementById('attendance-edit-form');
  if (!form) return;
  form.addEventListener('submit', function(ev){
    var dateEl = document.getElementById('att-edit-date');
    var date = dateEl ? String(dateEl.value).trim() : '';

    // reset decorations
    if(dateEl){ dateEl.style.borderColor=''; dateEl.style.boxShadow=''; }

    var errs = [];
    if (!date) errs.push('* Ngày không được để trống.');

    if (errs.length){
      ev.preventDefault();
      var title = (window.__MESSAGES__ && window.__MESSAGES__['error_notification_title']) ? window.__MESSAGES__['error_notification_title'] : 'THÔNG BÁO';
      var body  = errs.join('<br>');
      if (typeof window.showToast === 'function'){
        window.showToast('error', '<div class="error-header">'+title+'</div>'+body);
      } else {
        alert(errs.join('\n'));
      }
    }
  });
})();
</script>

<?php if ($__att_created): ?>
<script>
  document.addEventListener('DOMContentLoaded', function(){
    try {
      var title = (window.__MESSAGES__ && window.__MESSAGES__['success_notification_title']) ? window.__MESSAGES__['success_notification_title'] : 'THÔNG BÁO';
      var body = <?= json_encode($__att_created_msg, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
      if (typeof window.showToast === 'function') {
        window.showToast('success', '<div class="success-header">' + title + '</div>' + body);
      }
    } catch(e) {}
  });
</script>
<?php endif; ?>

<?php if ($__att_edited ?? false): ?>
<script>
  document.addEventListener('DOMContentLoaded', function(){
    try {
      var title = (window.__MESSAGES__ && window.__MESSAGES__['success_notification_title']) ? window.__MESSAGES__['success_notification_title'] : 'THÔNG BÁO';
      var body = <?= json_encode($__att_edited_msg ?? '', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
      if (typeof window.showToast === 'function') {
        window.showToast('success', '<div class="success-header">' + title + '</div>' + body);
      }
    } catch(e) {}
  });
</script>
<?php endif; ?>
