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

dashboard.php

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

global $pdo;
$pageTitle = 'Dashboard';
$currentUser = getCurrentUser();
$role = wp_normalize_role_name((string)($currentUser['role'] ?? ($_SESSION['user_role'] ?? '')));
$companyName = getSystemInfo('company_name', 'Company Name');

$canHolidayRequest = isAdminRole() || hasPermission('holidays.request') || hasPermission('holidays.manage');
$canIncidentCreate = isAdminRole() || hasPermission('incidents.create') || hasPermission('incidents.manage');
$canContactsView   = isAdminRole() || hasPermission('contacts.view') || hasPermission('contacts.manage');
$canRotaView       = isAdminRole() || hasPermission('rota.view');
$canManagement     = isAdminRole() || hasPermission('management.access');
$canAdmin          = isAdminRole();
$canCalendar       = isAdminRole() || hasPermission('calendar.view');
$canTruTrakMap     = hasPermission('trutrak.view');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $action = (string)($_POST['action'] ?? '');

    if ($action === 'dashboard_holiday_request' && $canHolidayRequest) {
        $start = trim((string)($_POST['start_date'] ?? ''));
        $end = trim((string)($_POST['end_date'] ?? ''));
        $reason = trim((string)($_POST['reason'] ?? ''));
        $priority = trim((string)($_POST['priority'] ?? 'Normal')) ?: 'Normal';
        $notes = trim((string)($_POST['notes'] ?? ''));
        if ($start === '' || $end === '') {
            wp_feature_flash_set('error', 'Start date and end date are required.');
            app_redirect('');
        }
        $days = max(1, (int)((strtotime($end) - strtotime($start)) / 86400) + 1);
        $code = wp_feature_generate_code('HOL', 'holiday_requests', 'request_code');
        $stmt = $pdo->prepare("INSERT INTO holiday_requests (request_code, user_id, username, role_name, start_date, end_date, total_days, reason, priority, status, notes, submitted_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'Pending', ?, NOW())");
        $stmt->execute([$code, wp_feature_current_user_id(), wp_feature_current_user_name(), $_SESSION['user_role'] ?? '', $start, $end, $days, $reason, $priority, $notes]);
        logActivity('holiday_request_created', 'holiday_request', (int)$pdo->lastInsertId(), 'Submitted holiday request ' . $code . ' from dashboard');
        wp_feature_flash_set('success', 'Holiday request sent.');
        app_redirect('');
    }

    if ($action === 'dashboard_incident_report' && $canIncidentCreate) {
        $incidentDate = trim((string)($_POST['incident_date'] ?? date('Y-m-d')));
        $driverChoice = trim((string)($_POST['driver_choice'] ?? ''));
        $driverName = trim((string)($_POST['driver_name'] ?? wp_feature_current_user_name()));
        if ($driverChoice !== '' && $driverChoice !== '__other__') $driverName = $driverChoice;
        $porterChoice = trim((string)($_POST['porter_choice'] ?? ''));
        $porterName = trim((string)($_POST['porter_name'] ?? ''));
        if ($porterChoice !== '' && $porterChoice !== '__other__') $porterName = $porterChoice;
        $vanChoice = trim((string)($_POST['van_choice'] ?? ''));
        $vanLabel = trim((string)($_POST['van_label'] ?? ''));
        if ($vanChoice !== '' && $vanChoice !== '__other__') $vanLabel = $vanChoice;
        $description = trim((string)($_POST['description'] ?? ''));
        $damageCost = (float)($_POST['damage_cost'] ?? 0);
        $notes = trim((string)($_POST['notes'] ?? ''));
        if ($description === '') {
            wp_feature_flash_set('error', 'Please add a short description of the issue.');
            app_redirect('');
        }
        $code = wp_feature_generate_code('INC', 'incidents', 'incident_code');
        $stmt = $pdo->prepare("INSERT INTO incidents (incident_code, incident_date, driver_user_id, driver_name, porter_user_id, porter_name, van_label, description, damage_cost, status, notes, created_by, created_by_name, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'Open', ?, ?, ?, NOW())");
        $stmt->execute([
            $code,
            $incidentDate,
            wp_feature_find_user_id($driverName),
            $driverName,
            wp_feature_find_user_id($porterName),
            $porterName,
            $vanLabel,
            $description,
            $damageCost,
            $notes,
            wp_feature_current_user_id(),
            wp_feature_current_user_name(),
        ]);
        $incidentId = (int)$pdo->lastInsertId();

        if (!empty($_FILES['photos']['name'][0])) {
            foreach ($_FILES['photos']['name'] as $i => $name) {
                if (empty($_FILES['photos']['tmp_name'][$i]) || !is_uploaded_file($_FILES['photos']['tmp_name'][$i])) continue;
                $tmp = $_FILES['photos']['tmp_name'][$i];
                $upload = wp_onedrive_upload_file($tmp, $name ?: ('incident_' . $incidentId . '_' . $i . '.jpg'), 'incidents');
                if (!empty($upload['success'])) {
                    $photoStmt = $pdo->prepare("INSERT INTO incident_photos (incident_id, storage, file_name, onedrive_item_id, onedrive_web_url, created_at) VALUES (?, 'onedrive', ?, ?, ?, NOW())");
                    $photoStmt->execute([$incidentId, $upload['name'] ?: $name, $upload['item_id'] ?? '', $upload['web_url'] ?? '']);
                }
            }
        }

        logActivity('incident_created', 'incident', $incidentId, 'Created incident ' . $code . ' from dashboard');
        wp_feature_flash_set('success', 'Incident report saved.');
        app_redirect('');
    }

}

$featureMetrics = wp_feature_dashboard_metrics();
$clockStatus = wp_clock_available_for_current_user() ? wp_clock_status() : ['status' => 'not_available'];
$clockRequired = wp_clock_required_for_current_user();
$clockState = (string)($clockStatus['status'] ?? 'not_clocked_in');
$clockPillClass = $clockState === 'clocked_in' ? 'on' : ($clockState === 'shift_ended' ? 'ended' : '');
$clockText = match ($clockState) {
    'clocked_in' => 'Currently on shift',
    'shift_ended' => 'Shift completed today',
    default => 'Not on shift yet',
};
$clockTile = null;
if (wp_clock_available_for_current_user()) {
    if ($clockState === 'clocked_in') {
        $clockTile = [
            'url' => app_url('clock'),
            'tile_class' => 'tile-out',
            'icon' => '๐Ÿ',
            'label' => 'End Shift',
            'sub' => 'Clock out to finish your shift',
        ];
    } else {
        $clockTile = [
            'url' => app_url('clock'),
            'tile_class' => 'tile-in',
            'icon' => 'โฑ๏ธ',
            'label' => 'Start Shift',
            'sub' => $clockState === 'shift_ended' ? 'Clock in to start another shift' : 'Clock in to start your shift',
        ];
    }
}

$myRota = $canRotaView ? wp_feature_fetch_rota_for_current_user(date('Y-m-d'), 12) : [];
$contacts = $canContactsView ? wp_feature_fetch_contacts(80) : [];
$dashboardUsers = wp_feature_users();
$dashboardVans = [];
try { $dashboardVans = $pdo->query("SELECT plate_full, plate_short, make, model FROM vans WHERE is_active = 1 ORDER BY plate_full ASC")->fetchAll(PDO::FETCH_ASSOC) ?: []; } catch (Throwable $e) {}
$alerts = (hasPermission('alerts.view') || isAdminRole()) ? wp_feature_fetch_user_alerts(20) : ['unread' => 0, 'items' => []];
$flash = wp_feature_flash_get();

$dashboardButtons = [];
if ($canCalendar) $dashboardButtons[] = ['label' => 'Calendar', 'url' => app_url('calendar'), 'class' => 'btn btn-secondary'];
if ($canTruTrakMap) $dashboardButtons[] = ['label' => 'TruTrak Map', 'url' => app_url('trutrak/map'), 'class' => 'btn btn-secondary'];
if ($canManagement) $dashboardButtons[] = ['label' => 'Management', 'url' => app_url('management'), 'class' => 'btn btn-primary'];
if ($canAdmin) $dashboardButtons[] = ['label' => 'Administration', 'url' => app_url('admin'), 'class' => 'btn btn-secondary'];

$extraHead = '<style>
.portal-wrap{max-width:1180px;margin:0 auto;width:100%;}
.portal-card{background:var(--card-bg, rgba(255,255,255,.02));border:1px solid var(--border);border-radius:22px;padding:20px;box-shadow:0 18px 40px rgba(0,0,0,.18);}
.portal-user{display:flex;gap:14px;align-items:flex-start;}
.portal-avatar{width:54px;height:54px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(255,140,26,.12);border:2px solid rgba(255,140,26,.65);font-weight:900;font-size:1.15rem;color:var(--accent);flex-shrink:0;}
.portal-name{font-size:1.55rem;font-weight:900;line-height:1.1;margin:0;}
.portal-role{font-size:.72rem;text-transform:uppercase;letter-spacing:3px;color:var(--accent);font-weight:800;margin-top:4px;}
.portal-status{display:inline-flex;align-items:center;gap:8px;margin-top:12px;padding:7px 12px;border-radius:999px;background:rgba(255,255,255,.03);border:1px solid var(--border);font-size:.92rem;color:var(--text-muted);}
.portal-status.on{background:rgba(34,197,94,.12);border-color:rgba(34,197,94,.25);color:#22c55e;}
.portal-status.ended{background:rgba(59,130,246,.12);border-color:rgba(59,130,246,.25);color:#60a5fa;}
.portal-status .dot{width:9px;height:9px;border-radius:50%;background:#6b7280;display:inline-block;}
.portal-status.on .dot{background:#22c55e;box-shadow:0 0 12px rgba(34,197,94,.55);}
.portal-status.ended .dot{background:#60a5fa;box-shadow:0 0 12px rgba(96,165,250,.45);}
.portal-actions-title{display:flex;align-items:center;gap:10px;margin:18px 4px 12px;color:var(--text-muted);font-size:.74rem;letter-spacing:4px;text-transform:uppercase;font-weight:700;}
.portal-actions-title::before,.portal-actions-title::after{content:"";height:1px;background:var(--border);flex:1;}
.portal-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;}
.portal-tile{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;min-height:168px;padding:22px 16px;border-radius:22px;border:1px solid var(--border);background:rgba(255,255,255,.02);color:var(--text-main);text-decoration:none;transition:transform .12s ease, border-color .12s ease, background .12s ease;}
.portal-tile:hover{transform:translateY(-1px);border-color:rgba(255,140,26,.35);background:rgba(255,255,255,.04);}
.portal-tile.disabled{opacity:.35;pointer-events:none;}
.portal-icon{width:58px;height:58px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.8rem;margin-bottom:14px;}
.tile-in .portal-icon{background:rgba(34,197,94,.12);} .tile-in .portal-label{color:#22c55e;}
.tile-out .portal-icon{background:rgba(239,68,68,.10);} .tile-out .portal-label{color:#ef4444;}
.tile-issue .portal-icon{background:rgba(255,140,26,.12);} .tile-issue .portal-label{color:var(--accent);}
.tile-holiday .portal-icon{background:rgba(59,130,246,.12);} .tile-holiday .portal-label{color:#60a5fa;}
.tile-rota .portal-icon{background:rgba(96,165,250,.12);} .tile-rota .portal-label{color:#93c5fd;}
.tile-contacts .portal-icon{background:rgba(236,72,153,.12);} .tile-contacts .portal-label{color:#f472b6;}
.portal-label{font-weight:900;font-size:1.05rem;letter-spacing:1px;}
.portal-sub{margin-top:6px;font-size:.9rem;color:var(--text-muted);}
.portal-links{display:flex;gap:10px;flex-wrap:wrap;margin-top:16px;}
.modal .form-text{color:var(--text-muted);} .modal-content{background:#121212;border:1px solid rgba(255,255,255,.08);}
.contact-list{display:grid;gap:10px;max-height:380px;overflow:auto;}
.contact-row{border:1px solid var(--border);border-radius:14px;padding:12px;background:rgba(255,255,255,.02);} 
.rota-list{display:grid;gap:10px;} .rota-row{border:1px solid var(--border);border-radius:14px;padding:12px;background:rgba(255,255,255,.02);} 
@media (max-width:1024px){.portal-grid{grid-template-columns:repeat(2,minmax(0,1fr));}.portal-wrap{max-width:900px;}}@media (max-width:640px){.portal-grid{grid-template-columns:1fr;}.portal-card{padding:16px;border-radius:18px;}.portal-actions-title{letter-spacing:3px;}.portal-tile{min-height:128px;padding:18px 14px;}.portal-name{font-size:1.3rem;}}
</style>';

include __DIR__ . '/../partials/header.php';
?>
<div class="portal-wrap">
    <?php if ($flash): ?>
        <div class="alert alert-<?= e($flash['type'] === 'error' ? 'error' : 'success') ?>" style="margin-bottom:16px;"><?= e($flash['message']) ?></div>
    <?php endif; ?>

    <div class="portal-card">
        <div class="portal-user">
            <div class="portal-avatar"><?= e(strtoupper(substr((string)($currentUser['display_name'] ?? 'U'), 0, 1))) ?></div>
            <div style="flex:1;min-width:0;">
                <h1 class="portal-name"><?= e((string)($currentUser['display_name'] ?? 'User')) ?></h1>
                <div class="portal-role"><?= e(ucwords(str_replace('-', ' ', (string)($currentUser['role'] ?? ($_SESSION['user_role'] ?? 'staff'))))) ?></div>
                <div class="portal-status <?= e($clockPillClass) ?>">
                    <span class="dot"></span>
                    <span><?= e($clockText) ?></span>
                </div>
                <div class="portal-links">
                    <?php foreach ($dashboardButtons as $button): ?>
                        <a href="<?= e($button['url']) ?>" class="<?= e($button['class']) ?>"><?= e($button['label']) ?></a>
                    <?php endforeach; ?>
                    <?php if (($alerts['unread'] ?? 0) > 0): ?><span class="btn btn-secondary disabled">Unread alerts: <?= (int)$alerts['unread'] ?></span><?php endif; ?>
                    <?php if ($clockRequired && $clockState !== 'clocked_in'): ?><span class="btn btn-secondary disabled">Clock in required before the rest of the app</span><?php endif; ?>
                </div>
            </div>
        </div>
    </div>

    <div class="portal-actions-title">Actions</div>

    <div class="portal-grid">
        <?php if ($clockTile): ?>
            <a href="<?= e($clockTile['url']) ?>" class="portal-tile <?= e($clockTile['tile_class']) ?>">
                <div class="portal-icon"><?= e($clockTile['icon']) ?></div>
                <div class="portal-label"><?= e($clockTile['label']) ?></div>
                <div class="portal-sub"><?= e($clockTile['sub']) ?></div>
            </a>
        <?php endif; ?>
        <button type="button" class="portal-tile tile-issue <?= $canIncidentCreate ? '' : 'disabled' ?>" data-wp-modal="incident">
            <div class="portal-icon">โš ๏ธ</div>
            <div class="portal-label">Report Issue</div>
            <div class="portal-sub">Send it straight to management</div>
        </button>
        <button type="button" class="portal-tile tile-holiday <?= $canHolidayRequest ? '' : 'disabled' ?>" data-wp-modal="holiday">
            <div class="portal-icon">๐Ÿ“…</div>
            <div class="portal-label">Holiday Request</div>
            <div class="portal-sub">Submit time off</div>
        </button>
        <button type="button" class="portal-tile tile-rota <?= $canRotaView ? '' : 'disabled' ?>" data-wp-modal="rota">
            <div class="portal-icon">๐Ÿ—“๏ธ</div>
            <div class="portal-label">My Rota</div>
            <div class="portal-sub"><?= e($myRota ? count($myRota) . ' upcoming shift(s)' : 'No shift attached') ?></div>
        </button>
        <button type="button" class="portal-tile tile-contacts <?= $canContactsView ? '' : 'disabled' ?>" data-wp-modal="contacts">
            <div class="portal-icon">๐Ÿ“ž</div>
            <div class="portal-label">Contacts</div>
            <div class="portal-sub"><?= e($contacts ? count($contacts) . ' saved contact(s)' : 'Open contact book') ?></div>
        </button>
    </div>
</div>


<style>
.dashboard-modal-form .form-label{font-weight:700;margin-bottom:6px;}
.dashboard-modal-form .form-control,.dashboard-modal-form textarea,.dashboard-modal-form select{background:#0f0f0f;border:1px solid rgba(255,255,255,.08);color:var(--text-main);}
.dashboard-modal-form .form-control:focus,.dashboard-modal-form textarea:focus,.dashboard-modal-form select:focus{background:#121212;color:var(--text-main);border-color:rgba(255,140,26,.4);box-shadow:0 0 0 .2rem rgba(255,140,26,.12);}
.dashboard-modal-stack{display:grid;gap:12px;max-height:min(62vh,560px);overflow:auto;padding-right:2px;}
.dashboard-modal-inline-footer{display:flex;gap:10px;justify-content:flex-end;align-items:center;margin-top:18px;flex-wrap:wrap;}
.dashboard-modal-inline-footer .btn{min-height:44px;}
.dashboard-empty{color:var(--text-muted);padding:8px 0;}
@media (max-width:640px){.dashboard-modal-inline-footer .btn,.dashboard-modal-inline-footer a.btn{width:100%;}.dashboard-modal-stack{max-height:none;}}
</style>

<div class="dashboard-modal-templates" hidden>
  <template id="tplHolidayModal">
    <form method="post" class="dashboard-modal-form">
      <input type="hidden" name="action" value="dashboard_holiday_request">
      <div class="mb-3"><label class="form-label">Start date</label><input class="form-control" type="date" name="start_date" required></div>
      <div class="mb-3"><label class="form-label">End date</label><input class="form-control" type="date" name="end_date" required></div>
      <div class="mb-3"><label class="form-label">Priority</label><select class="form-control" name="priority"><option>Normal</option><option>Important</option><option>Urgent</option></select></div>
      <div class="mb-3"><label class="form-label">Reason</label><textarea class="form-control" name="reason" rows="3" placeholder="Reason for leave"></textarea></div>
      <div><label class="form-label">Notes</label><textarea class="form-control" name="notes" rows="3" placeholder="Anything management should know"></textarea></div>
      <div class="form-text mt-2">Requests are approved or declined from the management holiday queue.</div>
      <div class="dashboard-modal-inline-footer">
        <button type="button" class="btn btn-secondary" data-close-dashboard-modal>Close</button>
        <button type="submit" class="btn btn-primary">Send request</button>
      </div>
    </form>
  </template>

  <template id="tplIncidentModal">
    <form method="post" enctype="multipart/form-data" class="dashboard-modal-form">
      <input type="hidden" name="action" value="dashboard_incident_report">
      <div class="row g-3">
        <div class="col-md-6"><label class="form-label">Incident date</label><input class="form-control" type="date" name="incident_date" value="<?= e(date('Y-m-d')) ?>"></div>
        <div class="col-md-6"><label class="form-label">Vehicle / Van</label><select class="form-control dash-other-select" name="van_choice" data-other-target="dashIncidentVanOther"><option value="">No vehicle</option><?php foreach ($dashboardVans as $v): ?><?php $vLabel = trim((string)($v['plate_short'] ?? '') . ' ' . (string)($v['plate_full'] ?? '')); ?><option value="<?= e($vLabel) ?>"><?= e($vLabel) ?><?= trim(($v['make'] ?? '') . ' ' . ($v['model'] ?? '')) ? ' ยท ' . e(trim(($v['make'] ?? '') . ' ' . ($v['model'] ?? ''))) : '' ?></option><?php endforeach; ?><option value="__other__">Other / manual input</option></select><input class="form-control mt-2" id="dashIncidentVanOther" type="text" name="van_label" placeholder="Manual vehicle/reg" style="display:none;"></div>
        <div class="col-md-6"><label class="form-label">Driver</label><select class="form-control dash-other-select" name="driver_choice" data-other-target="dashIncidentDriverOther"><?php foreach ($dashboardUsers as $u): ?><?php $uLabel = (string)($u['display_name'] ?: $u['username']); ?><option value="<?= e($uLabel) ?>" <?= $uLabel === wp_feature_current_user_name() ? 'selected' : '' ?>><?= e($uLabel) ?></option><?php endforeach; ?><option value="__other__">Other / manual input</option></select><input class="form-control mt-2" id="dashIncidentDriverOther" type="text" name="driver_name" value="<?= e(wp_feature_current_user_name()) ?>" placeholder="Manual driver" style="display:none;"></div>
        <div class="col-md-6"><label class="form-label">Porter</label><select class="form-control dash-other-select" name="porter_choice" data-other-target="dashIncidentPorterOther"><option value="">No porter</option><?php foreach ($dashboardUsers as $u): ?><?php $uLabel = (string)($u['display_name'] ?: $u['username']); ?><option value="<?= e($uLabel) ?>"><?= e($uLabel) ?></option><?php endforeach; ?><option value="__other__">Other / manual input</option></select><input class="form-control mt-2" id="dashIncidentPorterOther" type="text" name="porter_name" placeholder="Manual porter" style="display:none;"></div>
        <div class="col-12"><label class="form-label">What happened?</label><textarea class="form-control" name="description" rows="4" required placeholder="Describe the issue clearly"></textarea></div>
        <div class="col-md-6"><label class="form-label">Damage cost</label><input class="form-control" type="number" step="0.01" min="0" name="damage_cost" placeholder="0.00"></div>
        <div class="col-md-6"><label class="form-label">Photos</label><input class="form-control" type="file" name="photos[]" multiple></div>
        <div class="col-12"><label class="form-label">Notes</label><textarea class="form-control" name="notes" rows="3" placeholder="Follow-up notes"></textarea></div>
      </div>
      <div class="form-text mt-2">If OneDrive is configured the uploaded photos are pushed there and the file links are stored in the database.</div>
      <div class="dashboard-modal-inline-footer">
        <button type="button" class="btn btn-secondary" data-close-dashboard-modal>Close</button>
        <button type="submit" class="btn btn-primary">Save issue</button>
      </div>
    </form>
  </template>

  <template id="tplRotaModal">
    <?php if (!$canRotaView): ?>
      <div class="dashboard-empty">You do not currently have permission to view rota entries.</div>
    <?php elseif (!$myRota): ?>
      <div class="dashboard-empty">No rota entries are attached to your account yet.</div>
    <?php else: ?>
      <div class="dashboard-modal-stack">
        <?php foreach ($myRota as $entry): ?>
          <div class="rota-row">
            <div class="d-flex justify-content-between gap-3 flex-wrap"><strong><?= e((string)$entry['shift_date']) ?></strong><span class="text-muted"><?= e((string)($entry['role_label'] ?: 'Assigned')) ?></span></div>
            <div style="margin-top:6px;font-weight:700;"><?= e((string)($entry['route_name'] ?: 'Route not set')) ?></div>
            <div class="text-muted" style="margin-top:4px;"><?= e(trim((string)($entry['van_label'] ?: 'No van') . (($entry['notes'] ?? '') ? ' ยท ' . $entry['notes'] : ''))) ?></div>
          </div>
        <?php endforeach; ?>
      </div>
    <?php endif; ?>
    <div class="dashboard-modal-inline-footer">
      <button type="button" class="btn btn-secondary" data-close-dashboard-modal>Close</button>
      <a href="<?= e(app_url('management/rota?mine=1')) ?>" class="btn btn-primary">Open full rota</a>
    </div>
  </template>

  <template id="tplContactsModal">
    <?php if (!$canContactsView): ?>
      <div class="dashboard-empty">You do not currently have permission to view contacts.</div>
    <?php else: ?>
      <input class="form-control mb-3" id="contactSearchBox" type="text" placeholder="Search contacts by name, company or phone">
      <div class="contact-list dashboard-modal-stack" id="dashboardContactList">
        <?php foreach ($contacts as $contact): ?>
          <div class="contact-row" data-search="<?= e(strtolower(trim(($contact['name'] ?? '') . ' ' . ($contact['company'] ?? '') . ' ' . ($contact['phone'] ?? '') . ' ' . ($contact['email'] ?? '')))) ?>">
            <div class="d-flex justify-content-between gap-3 flex-wrap"><strong><?= e((string)$contact['name']) ?></strong><span class="text-muted"><?= e((string)($contact['type'] ?: 'Contact')) ?></span></div>
            <div class="text-muted" style="margin-top:4px;"><?= e(trim((string)($contact['position'] ?? '') . (($contact['company'] ?? '') ? ' ยท ' . $contact['company'] : ''))) ?></div>
          </div>
        <?php endforeach; ?>
      </div>
    <?php endif; ?>
    <div class="dashboard-modal-inline-footer">
      <button type="button" class="btn btn-secondary" data-close-dashboard-modal>Close</button>
      <a href="<?= e(app_url('management/contacts')) ?>" class="btn btn-primary">Manage contacts</a>
    </div>
  </template>
</div>

<script>
(function(){
  let dashboardModal = null;
  const modalMap = {
    holiday: { template: 'tplHolidayModal', title: 'Holiday Request', size: 'medium' },
    incident: { template: 'tplIncidentModal', title: 'Report Issue', size: 'large' },
    rota: { template: 'tplRotaModal', title: 'My Rota', size: 'large' },
    contacts: { template: 'tplContactsModal', title: 'Contacts', size: 'large' }
  };

  function destroyDashboardModal() {
    if (dashboardModal) {
      dashboardModal.close();
      dashboardModal = null;
    }
  }

  function filterDashboardContacts(term) {
    const scope = document.querySelector('.wp-modal-overlay.active') || document;
    const rows = scope.querySelectorAll('#dashboardContactList .contact-row');
    const q = (term || '').trim().toLowerCase();
    rows.forEach(function(row){
      row.style.display = !q || (row.dataset.search || '').includes(q) ? '' : 'none';
    });
  }
  window.filterDashboardContacts = filterDashboardContacts;

  document.addEventListener('click', function(ev){
    const closer = ev.target.closest('[data-close-dashboard-modal]');
    if (closer) {
      ev.preventDefault();
      destroyDashboardModal();
      return;
    }
    const trigger = ev.target.closest('[data-wp-modal]');
    if (!trigger) return;
    ev.preventDefault();
    if (trigger.classList.contains('disabled')) return;
    const key = trigger.getAttribute('data-wp-modal');
    const config = modalMap[key];
    if (!config) return;
    const tpl = document.getElementById(config.template);
    if (!tpl) return;
    destroyDashboardModal();
    dashboardModal = new Modal({
      title: config.title,
      content: tpl.innerHTML,
      size: config.size,
      buttons: [],
      onClose: function(){ dashboardModal = null; }
    });
    dashboardModal.open();
    document.querySelectorAll('.wp-modal-overlay.active .dash-other-select').forEach(function(sel){
      function syncOther(){ const target=document.getElementById(sel.dataset.otherTarget || ''); if(target) target.style.display = sel.value === '__other__' ? '' : 'none'; }
      sel.addEventListener('change', syncOther); syncOther();
    });
    const box = document.querySelector('.wp-modal-overlay.active #contactSearchBox');
    if (box) {
      box.addEventListener('input', function(){ filterDashboardContacts(this.value); });
    }
  });

  window.addEventListener('pageshow', function(){
    document.querySelectorAll('.wp-modal-overlay').forEach(function(el){ el.remove(); });
    document.body.style.removeProperty('overflow');
  });
})();
</script>
<?php include __DIR__ . '/../partials/footer.php'; ?>