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

staff_management.php

Type
php
Size
28.94 KB
Modified
15 May
staff_management.php 28.94 KB
<?php
require_once __DIR__ . '/../bootstrap.php';

requireAdmin();

$pageTitle = 'Staff Management';

$msg = [];
$err = [];

// Get current tab
$tab = $_GET['tab'] ?? 'staff';

// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    
    // Create/Update User
    if (isset($_POST['action']) && $_POST['action'] === 'save_user') {
        $userId = isset($_POST['user_id']) && $_POST['user_id'] ? (int)$_POST['user_id'] : null;
        $username = trim($_POST['username'] ?? '');
        $email = trim($_POST['email'] ?? '');
        $displayName = trim($_POST['display_name'] ?? '');
        $role = trim($_POST['role'] ?? '');
        $secondaryGroup = trim($_POST['secondary_group'] ?? '');
        $isActive = isset($_POST['is_active']) ? 1 : 0;
        $password = trim($_POST['password'] ?? '');
        
        // Validation
        if (!$username || !$email || !$displayName || !$role) {
            $err[] = 'All fields are required';
        }
        
        // Validate role
        $allowedRoles = ['administrator', 'director', 'manager', 'driver', 'porter'];
        if (!in_array($role, $allowedRoles)) {
            $err[] = 'Invalid role selected';
        }
        
        // Validate secondary group (only for non-driver/porter roles)
        if (!in_array($role, ['driver', 'porter'])) {
            if (!$secondaryGroup || !in_array($secondaryGroup, ['office', 'driver', 'porter'])) {
                $err[] = 'Secondary group is required for non-driver/porter roles';
            }
        } else {
            // Drivers and porters don't need secondary group
            $secondaryGroup = null;
        }
        
        if (empty($err)) {
            try {
                if ($userId) {
                    // Update existing user
                    if ($password) {
                        $passwordHash = password_hash($password, PASSWORD_DEFAULT);
                        $stmt = $pdo->prepare("UPDATE users SET username=?, email=?, display_name=?, role=?, secondary_group=?, is_active=?, password_hash=? WHERE id=?");
                        $stmt->execute([$username, $email, $displayName, $role, $secondaryGroup, $isActive, $passwordHash, $userId]);
                    } else {
                        $stmt = $pdo->prepare("UPDATE users SET username=?, email=?, display_name=?, role=?, secondary_group=?, is_active=? WHERE id=?");
                        $stmt->execute([$username, $email, $displayName, $role, $secondaryGroup, $isActive, $userId]);
                    }
                    $msg[] = 'User updated successfully';
                    logActivity('users.update', 'user', $userId, "Updated user: $username");
                } else {
                    // Create new user
                    if (!$password) {
                        $err[] = 'Password is required for new users';
                    } else {
                        $passwordHash = password_hash($password, PASSWORD_DEFAULT);
                        $stmt = $pdo->prepare("INSERT INTO users (username, email, display_name, role, secondary_group, is_active, password_hash) VALUES (?, ?, ?, ?, ?, ?, ?)");
                        $stmt->execute([$username, $email, $displayName, $role, $secondaryGroup, $isActive, $passwordHash]);
                        $userId = $pdo->lastInsertId();
                        $msg[] = 'User created successfully';
                        logActivity('users.create', 'user', $userId, "Created user: $username");
                    }
                }
            } catch (PDOException $e) {
                if (strpos($e->getMessage(), 'Duplicate entry') !== false) {
                    $err[] = 'Username or email already exists';
                } else {
                    $err[] = 'Database error: ' . $e->getMessage();
                }
            }
        }
    }
    
    // Delete User
    if (isset($_POST['action']) && $_POST['action'] === 'delete_user') {
        $userId = (int)$_POST['user_id'];
        
        // Prevent deleting yourself
        if ($userId == $_SESSION['user_id']) {
            $err[] = 'You cannot delete your own account';
        } else {
            try {
                $stmt = $pdo->prepare("DELETE FROM users WHERE id=?");
                $stmt->execute([$userId]);
                $msg[] = 'User deleted successfully';
                logActivity('users.delete', 'user', $userId, 'Deleted user');
            } catch (PDOException $e) {
                $err[] = 'Failed to delete user: ' . $e->getMessage();
            }
        }
    }
    
    // Update Role Permissions
    if (isset($_POST['action']) && $_POST['action'] === 'update_permissions') {
        $roleId = (int)$_POST['role_id'];
        $permissions = $_POST['permissions'] ?? [];
        $roleName = '';

        try {
            $roleStmt = $pdo->prepare("SELECT name FROM roles WHERE id=? LIMIT 1");
            $roleStmt->execute([$roleId]);
            $roleName = wp_normalize_role_name((string)($roleStmt->fetchColumn() ?: ''));

            // Driver and Porter roles are intentionally blocked from Calendar access.
            if (in_array($roleName, ['driver', 'porter'], true) && !empty($permissions)) {
                $blockedPermissionIds = [];
                $blockStmt = $pdo->query("SELECT id FROM permissions WHERE name LIKE 'calendar.%'");
                if ($blockStmt) {
                    $blockedPermissionIds = array_map('intval', $blockStmt->fetchAll(PDO::FETCH_COLUMN));
                }
                if (!empty($blockedPermissionIds)) {
                    $permissions = array_values(array_filter($permissions, static function ($permId) use ($blockedPermissionIds) {
                        return !in_array((int)$permId, $blockedPermissionIds, true);
                    }));
                }
            }

            // Delete existing permissions for this role
            $stmt = $pdo->prepare("DELETE FROM role_permissions WHERE role_id=?");
            $stmt->execute([$roleId]);
            
            // Insert new permissions
            if (!empty($permissions)) {
                $stmt = $pdo->prepare("INSERT INTO role_permissions (role_id, permission_id) VALUES (?, ?)");
                foreach ($permissions as $permId) {
                    $stmt->execute([$roleId, (int)$permId]);
                }
            }
            
            $msg[] = 'Permissions updated successfully';
            logActivity('roles.update', 'role', $roleId, 'Updated role permissions');
        } catch (PDOException $e) {
            $err[] = 'Failed to update permissions: ' . $e->getMessage();
        }
    }
}

// Fetch all users
$users = [];
try {
    $stmt = $pdo->query("SELECT id, username, display_name, email, role, secondary_group, is_active, created_at FROM users ORDER BY is_active DESC, display_name ASC");
    $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
    error_log("Users fetch error: " . $e->getMessage());
}

// Fetch all roles
$roles = [];
try {
    $stmt = $pdo->query("SELECT id, name, display_name FROM roles WHERE name IN ('administrator', 'director', 'manager', 'driver', 'porter') ORDER BY FIELD(name, 'administrator', 'director', 'manager', 'driver', 'porter')");
    $roles = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
    error_log("Roles fetch error: " . $e->getMessage());
}

// Fetch all permissions
$permissions = [];
try {
    $stmt = $pdo->query("SELECT id, name, display_name, category FROM permissions ORDER BY category, display_name");
    $permissions = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
    error_log("Permissions fetch error: " . $e->getMessage());
}

// Fetch role permissions
$rolePermissions = [];
try {
    $stmt = $pdo->query("SELECT role_id, permission_id FROM role_permissions");
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    foreach ($result as $row) {
        if (!isset($rolePermissions[$row['role_id']])) {
            $rolePermissions[$row['role_id']] = [];
        }
        $rolePermissions[$row['role_id']][] = $row['permission_id'];
    }
} catch (Throwable $e) {
    error_log("Role permissions fetch error: " . $e->getMessage());
}

$permissionDescriptions = [
    'dashboard.view' => 'Can open the main dashboard.',
    'management.access' => 'Can open the management area and its operational pages.',
    'calendar.view' => 'Can view the calendar.',
    'calendar.create' => 'Can add calendar events.',
    'calendar.edit' => 'Can edit calendar events.',
    'calendar.delete' => 'Can delete calendar events.',
    'calendar.categories.manage' => 'Can manage calendar categories and defaults.',
    'staff.view' => 'Can view staff lists and profiles.',
    'staff.edit' => 'Can create, edit and remove staff accounts.',
    'vehicles.view' => 'Can view vehicles.',
    'vehicles.edit' => 'Can add and edit vehicles.',
    'trutrak.view' => 'Can view the TruTrak Map.',
    'trutrak.history' => 'Can view vehicle history in TruTrak.',
    'trutrak.manage' => 'Can edit TruTrak connector settings.',
    'rota.view' => 'Can view rota entries.',
    'rota.edit' => 'Can create and edit rota entries.',
    'clock.use' => 'Can use the shift clock.',
    'clock.driver_start' => 'Can complete driver start-of-shift details and checks.',
    'clock.porter_start' => 'Can complete porter start-of-shift confirmations.',
    'clock.view_team' => 'Can review team clock records for the selected day.',
    'clock.driver_checkout' => 'Can complete driver end-of-shift checks.',
    'clock.porter_checkout' => 'Can complete porter end-of-shift checks.',
    'clock.photo_upload' => 'Can upload required shift clock photos.',
    'holidays.request' => 'Can submit holiday requests.',
    'holidays.view' => 'Can view holiday requests in the management queue.',
    'holidays.manage' => 'Can approve or decline holiday requests.',
    'incidents.create' => 'Can report incidents.',
    'incidents.view' => 'Can view incident records.',
    'incidents.manage' => 'Can review and update incidents.',
    'contacts.view' => 'Can open the shared contacts book.',
    'contacts.manage' => 'Can add and edit contacts.',
    'alerts.view' => 'Can open the alerts centre and read alerts.',
    'alerts.send' => 'Can send alerts to staff.',
    'exports.manage' => 'Can open the exports area.',
    'exports.calendar' => 'Can export calendar data.',
    'exports.clock' => 'Can export clocking data.',
    'exports.rota' => 'Can export rota data.',
    'exports.holidays' => 'Can export holiday data.',
    'exports.incidents' => 'Can export incident data.',
    'integrations.manage' => 'Can configure OneDrive and other integrations.',
    'logs.view' => 'Can review activity logs.',
];

$roleGuidance = [
    'administrator' => 'Full control of the app, system setup and maintenance.',
    'director' => 'Full operational and reporting access, including admin pages.',
    'manager' => 'Runs daily operations: vehicles, rota, holidays, incidents, contacts, alerts and team clock records. Staff accounts stay in Administration.',
    'driver' => 'Front-line access only: dashboard, shift clock, own rota, holiday requests, contacts, alerts and incident reporting. Driver clock out can require checks and photos. Calendar is system-blocked for this role.',
    'porter' => 'Front-line access only: dashboard, shift clock, own rota, holiday requests, contacts, alerts and incident reporting. Porter clock out can require completion checks. Calendar is system-blocked for this role.',
];

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

<div class="content-header">
    <div>
        <h1 class="content-title">👥 Staff Management</h1>
        <p class="content-subtitle">Manage staff accounts and role permissions</p>
    </div>
    <div style="display: flex; gap: 12px;">
        <a href="?tab=staff" class="btn <?= $tab === 'staff' ? 'btn-primary' : 'btn-secondary' ?>">
            👥 Staff Management
        </a>
        <a href="?tab=permissions" class="btn <?= $tab === 'permissions' ? 'btn-primary' : 'btn-secondary' ?>">
            🔐 Role Permissions
        </a>
    </div>
</div>

<?php if ($msg): foreach ($msg as $m): ?>
    <div class="alert alert-success"><?= htmlspecialchars($m) ?></div>
<?php endforeach; endif; ?>

<?php if ($err): foreach ($err as $e): ?>
    <div class="alert alert-error"><?= htmlspecialchars($e) ?></div>
<?php endforeach; endif; ?>

<?php if ($tab === 'staff'): ?>
<!-- Staff Management Tab -->
<div class="card">
    <div class="card-header">
        <h3 class="card-title">All Staff (<?= count($users) ?>)</h3>
        <button class="btn btn-primary" type="button" onclick="openUserModal()">
            ➕ Add Staff
        </button>
    </div>
    <div class="card-body">
        <?php if (empty($users)): ?>
            <p class="text-muted">No staff members found.</p>
        <?php else: ?>
            <div class="table-wrap">
            <table class="table">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Username</th>
                        <th>Display Name</th>
                        <th>Email</th>
                        <th>Role</th>
                        <th>Status</th>
                        <th>Actions</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($users as $user): ?>
                    <tr>
                        <td><?= (int)$user['id'] ?></td>
                        <td><?= htmlspecialchars($user['username']) ?></td>
                        <td><?= htmlspecialchars($user['display_name']) ?></td>
                        <td><?= htmlspecialchars($user['email']) ?></td>
                        <td>
                            <span class="badge badge-secondary">
                                <?= htmlspecialchars(ucwords(str_replace('-', ' ', $user['role']))) ?>
                            </span>
                        </td>
                        <td>
                            <?php if ($user['is_active']): ?>
                                <span class="badge badge-success">Active</span>
                            <?php else: ?>
                                <span class="badge badge-danger">Inactive</span>
                            <?php endif; ?>
                        </td>
                        <td>
                            <div style="display: flex; gap: 6px;">
                                <button class="btn btn-xs btn-secondary" onclick='editUser(<?= json_encode($user) ?>)'>Edit</button>
                                <?php if ($user['id'] != $_SESSION['user_id']): ?>
                                <form method="POST" style="margin: 0;" onsubmit="return confirm('Delete this user? This cannot be undone.');">
                                    <input type="hidden" name="action" value="delete_user">
                                    <input type="hidden" name="user_id" value="<?= $user['id'] ?>">
                                    <button type="submit" class="btn btn-danger btn-xs">🗑</button>
                                </form>
                                <?php endif; ?>
                            </div>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
            </div>
        <?php endif; ?>
    </div>
</div>

<!-- User Modal -->
<div class="modal-overlay" id="userModal" style="display: none;">
    <div class="modal modal-medium">
        <div class="modal-header">
            <h3 class="modal-title" id="userModalTitle">Create Staff</h3>
            <button class="modal-close" type="button" onclick="closeUserModal()">×</button>
        </div>
        <form method="POST" id="userForm">
            <input type="hidden" name="action" value="save_user">
            <input type="hidden" name="user_id" id="user_id">
            <div class="modal-body">
                <div class="form-group">
                    <label>Username *</label>
                    <input type="text" name="username" id="username" class="form-control" required>
                </div>
                <div class="form-group">
                    <label>Display Name *</label>
                    <input type="text" name="display_name" id="display_name" class="form-control" required>
                </div>
                <div class="form-group">
                    <label>Email *</label>
                    <input type="email" name="email" id="email" class="form-control" required>
                </div>
                <div class="form-group">
                    <label>Password <span id="passwordHint">(leave blank to keep current)</span></label>
                    <input type="password" name="password" id="password" class="form-control">
                </div>
                <div class="form-group">
                    <label>Role *</label>
                    <select name="role" id="role" class="form-control" required onchange="toggleSecondaryGroup()">
                        <option value="">Select Role</option>
                        <option value="administrator">Administrator</option>
                        <option value="director">Director</option>
                        <option value="manager">Manager</option>
                                                <option value="driver">Driver</option>
                        <option value="porter">Porter</option>
                    </select>
                </div>
                <div class="form-group" id="secondaryGroupField" style="display: none;">
                    <label>Secondary Group * <small>(Required for non-driver/porter roles)</small></label>
                    <select name="secondary_group" id="secondary_group" class="form-control">
                        <option value="">Select Group</option>
                        <option value="office">Office</option>
                        <option value="driver">Driver</option>
                        <option value="porter">Porter</option>
                    </select>
                    <p class="small text-muted" style="margin-top: 8px;">
                        <strong>Office:</strong> Can only be assigned as office staff in calendar<br>
                        <strong>Driver:</strong> Can be assigned as driver in calendar<br>
                        <strong>Porter:</strong> Can be assigned as porter in calendar
                    </p>
                </div>
                <div class="form-group">
                    <label>
                        <input type="checkbox" name="is_active" id="is_active" checked>
                        Active
                    </label>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" onclick="closeUserModal()">Cancel</button>
                <button type="submit" class="btn btn-primary">Save</button>
            </div>
        </form>
    </div>
</div>

<script>
function toggleSecondaryGroup() {
    const role = document.getElementById('role').value;
    const field = document.getElementById('secondaryGroupField');
    const select = document.getElementById('secondary_group');
    
    if (role && role !== 'driver' && role !== 'porter') {
        // Show secondary group field for non-driver/porter roles
        field.style.display = 'block';
        select.required = true;
    } else {
        // Hide for driver/porter roles
        field.style.display = 'none';
        select.required = false;
        select.value = '';
    }
}

function openUserModal() {
    document.getElementById('userModalTitle').textContent = 'Create Staff';
    document.getElementById('userForm').reset();
    document.getElementById('user_id').value = '';
    document.getElementById('passwordHint').textContent = '*';
    document.getElementById('password').required = true;
    document.getElementById('secondaryGroupField').style.display = 'none';
    document.getElementById('secondary_group').required = false;
    document.getElementById('userModal').style.display = 'flex';
}

function editUser(user) {
    document.getElementById('userModalTitle').textContent = 'Edit Staff';
    document.getElementById('user_id').value = user.id;
    document.getElementById('username').value = user.username;
    document.getElementById('display_name').value = user.display_name;
    document.getElementById('email').value = user.email;
    document.getElementById('role').value = user.role;
    document.getElementById('secondary_group').value = user.secondary_group || '';
    document.getElementById('is_active').checked = user.is_active == 1;
    document.getElementById('password').value = '';
    document.getElementById('passwordHint').textContent = '(leave blank to keep current)';
    document.getElementById('password').required = false;
    toggleSecondaryGroup(); // Show/hide secondary group based on role
    document.getElementById('userModal').style.display = 'flex';
}

function closeUserModal() {
    document.getElementById('userModal').style.display = 'none';
}
</script>

<?php else: ?>
<!-- Role Permissions Tab -->
<div class="card">
    <div class="card-header">
        <h3 class="card-title">🔐 Role Permissions</h3>
    </div>
    <div class="card-body">
        <?php if (empty($roles)): ?>
            <p class="text-muted">No roles found.</p>
        <?php else: ?>
            <?php foreach ($roles as $role): ?>
            <?php
                $roleName = wp_normalize_role_name((string)$role['name']);
                $systemRole = in_array($roleName, ['administrator', 'director'], true);
                $calendarBlockedRole = in_array($roleName, ['driver', 'porter'], true);
                $enabledCount = isset($rolePermissions[$role['id']]) ? count($rolePermissions[$role['id']]) : 0;
                $totalCount = count($permissions);
            ?>
            <div class="card perm-role-card <?= $systemRole ? 'collapsed' : '' ?>" style="margin-bottom: 20px;">
                <div class="card-header perm-role-header">
                    <button type="button" class="perm-role-toggle" aria-expanded="<?= $systemRole ? 'false' : 'true' ?>" aria-controls="perm-body-<?= (int)$role['id'] ?>">
                        <span class="perm-role-title"><?= htmlspecialchars($role['display_name']) ?></span>
                        <?php if ($systemRole): ?>
                            <span class="badge badge-success">System controlled</span>
                            <span class="perm-count">Full access</span>
                        <?php else: ?>
                            <?php if ($calendarBlockedRole): ?><span class="badge badge-warning">Calendar blocked</span><?php endif; ?>
                            <span class="perm-count"><?= (int)$enabledCount ?> / <?= (int)$totalCount ?></span>
                        <?php endif; ?>
                    </button>
                    <div class="text-muted" style="padding:0 16px 14px 16px;"><?= htmlspecialchars($roleGuidance[$roleName] ?? '') ?></div>
                    <?php if (!$systemRole): ?>
                        <button type="submit" class="btn btn-primary" form="perm-form-<?= (int)$role['id'] ?>">Save Permissions</button>
                    <?php endif; ?>
                </div>
                <div class="card-body">
                    <div class="perm-role-card-body" id="perm-body-<?= (int)$role['id'] ?>">
                        <div class="perm-role-body">
                            <?php if ($systemRole): ?>
                                <div class="alert alert-info" style="margin:0;">
                                    <strong><?= htmlspecialchars($role['display_name']) ?> permissions are system controlled.</strong><br>
                                    This role always has full app access and does not need individual permission checkboxes saved.
                                </div>
                            <?php else: ?>
                                <form method="POST" id="perm-form-<?= (int)$role['id'] ?>">
                                    <input type="hidden" name="action" value="update_permissions">
                                    <input type="hidden" name="role_id" value="<?= (int)$role['id'] ?>">

                                    <?php
                                    $permissionByName = [];
                                    foreach ($permissions as $perm) {
                                        $permissionByName[$perm['name']] = $perm;
                                    }

                                    $permissionGroups = [
                                        'Navigation & Access' => ['dashboard.view', 'management.access'],
                                        'Calendar' => ['calendar.view', 'calendar.create', 'calendar.edit', 'calendar.delete', 'calendar.categories.manage'],
                                        'Staff' => ['staff.view', 'staff.edit'],
                                        'Vehicles & Tracking' => ['vehicles.view', 'vehicles.edit', 'trutrak.view', 'trutrak.history', 'trutrak.manage'],
                                        'Rota & Clocking' => ['rota.view', 'rota.edit', 'clock.use', 'clock.view_team', 'clock.driver_start', 'clock.driver_checkout', 'clock.porter_start', 'clock.porter_checkout', 'clock.photo_upload'],
                                        'Holiday Requests' => ['holidays.request', 'holidays.view', 'holidays.manage'],
                                        'Incidents' => ['incidents.create', 'incidents.view', 'incidents.manage'],
                                        'Contacts & Alerts' => ['contacts.view', 'contacts.manage', 'alerts.view', 'alerts.send'],
                                        'Exports & System' => ['exports.manage', 'exports.calendar', 'exports.clock', 'exports.rota', 'exports.holidays', 'exports.incidents', 'integrations.manage', 'logs.view'],
                                    ];

                                    if ($calendarBlockedRole) {
                                        unset($permissionGroups['Calendar']);
                                    }
                                    ?>

                                    <?php if ($calendarBlockedRole): ?>
                                        <div class="alert alert-warning">
                                            Calendar access is disabled for <?= htmlspecialchars($role['display_name']) ?> by system policy, so Calendar permissions are not shown or saved for this role.
                                        </div>
                                    <?php endif; ?>

                                    <div class="grid-2 perm-grid">
                                        <?php foreach ($permissionGroups as $category => $permissionNames): ?>
                                            <div class="perm-group-card">
                                                <div class="perm-group-header">
                                                    <span><?= htmlspecialchars($category) ?></span>
                                                    <span class="perm-count"><?= (int)count($permissionNames) ?></span>
                                                </div>
                                                <div class="perm-items">
                                                    <?php foreach ($permissionNames as $permissionName): ?>
                                                        <?php if (empty($permissionByName[$permissionName])) continue; $perm = $permissionByName[$permissionName]; ?>
                                                        <label class="perm-item">
                                                            <input type="checkbox"
                                                                   name="permissions[]"
                                                                   value="<?= (int)$perm['id'] ?>"
                                                                   <?= isset($rolePermissions[$role['id']]) && in_array($perm['id'], $rolePermissions[$role['id']]) ? 'checked' : '' ?>>
                                                            <span><?= htmlspecialchars($perm['display_name']) ?><small class="text-muted" style="display:block;font-size:.78rem;line-height:1.35;"><?= htmlspecialchars($permissionDescriptions[$perm['name']] ?? $perm['name']) ?></small></span>
                                                        </label>
                                                    <?php endforeach; ?>
                                                </div>
                                            </div>
                                        <?php endforeach; ?>
                                    </div>
                                </form>
                            <?php endif; ?>
                        </div>
                    </div>
                </div>
            </div>
            <?php endforeach; ?>
        <?php endif; ?>
    </div>
</div>
<?php endif; ?>


<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.perm-role-toggle').forEach(function (btn) {
    btn.addEventListener('click', function () {
      var card = btn.closest('.perm-role-card');
      if (!card) return;
      var collapsed = card.classList.toggle('collapsed');
      btn.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
    });
  });
});
</script>

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