import { apiGet, apiPost, apiDelete } from '../api.js';
import { getRole, getUser, showToast } from '../app.js';
// Roles an admin can create; supervisors can only create coaches
const CREATEABLE_ROLES = {
admin: ['admin', 'supervisor', 'coach'],
supervisor: ['coach'],
};
let usersList = [];
let supervisorsList = [];
let callerRole = '';
export async function usersPage() {
callerRole = getRole();
const app = document.getElementById('app');
app.innerHTML = `
`;
document.getElementById('showCreateFormBtn').addEventListener('click', () => {
const wrapper = document.getElementById('createUserWrapper');
if (wrapper.style.display === 'none') {
showCreateForm();
} else {
hideCreateForm();
}
});
await loadUsers();
}
async function loadUsers() {
try {
const data = await apiGet('users.php');
usersList = data.users || [];
supervisorsList = data.supervisors || [];
callerRole = data.callerRole || callerRole;
renderUsers();
} catch (e) {
showToast(e.message, 'error');
document.getElementById('usersContent').innerHTML =
`${esc(e.message)}
`;
}
}
// ── User table ────────────────────────────────────────────────────────────
function renderUsers() {
const container = document.getElementById('usersContent');
const myUsername = getUser();
if (!usersList.length) {
container.innerHTML = `
No users found
${callerRole === 'supervisor' ? 'You have no coaches yet.' : 'Create the first user above.'}
`;
return;
}
// Group by role for a cleaner display
const byRole = { admin: [], supervisor: [], coach: [] };
usersList.forEach(u => (byRole[u.role] || (byRole.other = byRole.other || [])).push(u));
// Which role groups to show
const groups = callerRole === 'supervisor'
? [{ label: 'Coaches', key: 'coach' }]
: [
{ label: 'Admins', key: 'admin' },
{ label: 'Supervisors', key: 'supervisor' },
{ label: 'Coaches', key: 'coach' },
];
container.innerHTML = groups.map(group => {
const users = byRole[group.key] || [];
if (!users.length) return '';
return `
${group.label} (${users.length})
| Username |
${group.key !== 'coach' ? 'Location | ' : 'Supervisor | '}
Password Change Required |
Created |
Actions |
${users.map(u => userRowHTML(u, myUsername)).join('')}
`;
}).join('');
container.querySelectorAll('.delete-user-btn').forEach(btn => {
btn.addEventListener('click', () => deleteUser(btn.dataset.id, btn.dataset.name));
});
}
function userRowHTML(u, myUsername) {
const isSelf = u.username === myUsername;
const detail = u.role === 'coach'
? esc(u.supervisorUsername || '—')
: esc(u.location || '—');
const created = u.createdAt
? new Date(parseInt(u.createdAt, 10) * 1000).toLocaleDateString()
: '—';
const pwBadge = u.mustChangePassword == 1
? 'Pending'
: 'No';
const canDelete = !isSelf && (
callerRole === 'admin' ||
(callerRole === 'supervisor' && u.role === 'coach')
);
return `
|
${esc(u.username)}
${isSelf ? 'You' : ''}
|
${detail} |
${pwBadge} |
${created} |
${canDelete
? ``
: ''}
|
`;
}
async function deleteUser(userID, username) {
if (!confirm(`Delete user "${username}"? This cannot be undone.`)) return;
try {
const data = await apiDelete('users.php', { userID });
if (data.success) {
usersList = usersList.filter(u => u.userID !== userID);
showToast(`User "${username}" deleted`, 'success');
renderUsers();
}
} catch (e) {
showToast(e.message, 'error');
}
}
// ── Create user form ──────────────────────────────────────────────────────
function showCreateForm() {
const wrapper = document.getElementById('createUserWrapper');
const allowedRoles = CREATEABLE_ROLES[callerRole] || ['coach'];
const isSupervisor = callerRole === 'supervisor';
wrapper.style.display = '';
wrapper.innerHTML = `
`;
document.getElementById('cu_username').focus();
// Show/hide location vs supervisor field based on role selection (admin only)
if (!isSupervisor) {
document.getElementById('cu_role').addEventListener('change', (e) => {
const r = e.target.value;
document.getElementById('cu_location_row').style.display = r !== 'coach' ? '' : 'none';
document.getElementById('cu_supervisor_row').style.display = r === 'coach' ? '' : 'none';
});
// Set initial state based on default selection
const initRole = document.getElementById('cu_role').value;
document.getElementById('cu_location_row').style.display = initRole !== 'coach' ? '' : 'none';
document.getElementById('cu_supervisor_row').style.display = initRole === 'coach' ? '' : 'none';
}
document.getElementById('cu_submit').addEventListener('click', submitCreateUser);
document.getElementById('cu_cancel').addEventListener('click', hideCreateForm);
// Enter key on last visible field submits
wrapper.querySelectorAll('input').forEach(inp => {
inp.addEventListener('keydown', (e) => {
if (e.key === 'Enter') submitCreateUser();
if (e.key === 'Escape') hideCreateForm();
});
});
}
function hideCreateForm() {
const wrapper = document.getElementById('createUserWrapper');
if (wrapper) { wrapper.style.display = 'none'; wrapper.innerHTML = ''; }
}
async function submitCreateUser() {
const errEl = document.getElementById('cu_error');
errEl.style.display = 'none';
const isSupervisor = callerRole === 'supervisor';
const username = document.getElementById('cu_username').value.trim();
const password = document.getElementById('cu_password').value;
const role = isSupervisor ? 'coach' : (document.getElementById('cu_role').value || 'coach');
const mustChange = document.getElementById('cu_mustchange').checked ? 1 : 0;
let location = '';
let supervisorID = '';
if (!isSupervisor) {
if (role !== 'coach') {
location = document.getElementById('cu_location').value.trim();
} else {
supervisorID = document.getElementById('cu_supervisor_select').value;
}
}
if (!username) { showError(errEl, 'Username is required'); return; }
if (password.length < 6) { showError(errEl, 'Password must be at least 6 characters'); return; }
if (role === 'coach' && !isSupervisor && !supervisorID) {
showError(errEl, 'Please select a supervisor for this coach');
return;
}
const btn = document.getElementById('cu_submit');
btn.disabled = true;
btn.textContent = 'Creating...';
try {
const data = await apiPost('users.php', {
username, password, role, location, supervisorID, mustChangePassword: mustChange,
});
if (!data.success) throw new Error(data.error || 'Failed to create user');
// Add to local list and re-render
usersList.push({
userID: data.userID,
username: data.username,
role: data.role,
entityID: data.entityID,
location: data.location || '',
supervisorID: data.supervisorID || null,
supervisorUsername: data.supervisorUsername || null,
mustChangePassword: data.mustChangePassword,
createdAt: data.createdAt,
});
showToast(`User "${data.username}" created`, 'success');
hideCreateForm();
renderUsers();
} catch (e) {
showError(errEl, e.message);
btn.disabled = false;
btn.textContent = 'Create User';
}
}
function showError(el, msg) {
el.textContent = msg;
el.style.display = '';
}
function esc(s) {
const d = document.createElement('div');
d.textContent = s ?? '';
return d.innerHTML;
}