import { apiGet, apiPost } from '../api.js'; import { pageHeaderHTML, showToast } from '../app.js'; import { confirmAction } from '../confirm-modal.js'; const CLIENT_PAGE_SIZE = 50; let coaches = []; let clients = []; let selectedCoachID = null; let filterCoachSearch = ''; let clientsByCoach = {}; let clientPage = 0; let selectedClientCodes = new Set(); export async function assignmentsPage() { selectedCoachID = null; filterCoachSearch = ''; clientPage = 0; selectedClientCodes = new Set(); const app = document.getElementById('app'); app.innerHTML = ` ${pageHeaderHTML('Assign Clients')}
`; try { const data = await apiGet('assignments.php'); coaches = data.coaches || []; clients = data.clients || []; rebuildClientsByCoach(); renderAssignments(); } catch (e) { showToast(e.message, 'error'); document.getElementById('assignContent').innerHTML = `

${esc(e.message)}

`; } } function rebuildClientsByCoach() { clientsByCoach = {}; clients.forEach(c => { if (c.coachID) { (clientsByCoach[c.coachID] = clientsByCoach[c.coachID] || []).push(c); } }); } function renderAssignments() { const container = document.getElementById('assignContent'); if (!coaches.length) { container.innerHTML = `

No counselors found

Create counselors first before assigning clients.

`; return; } const unassignedCount = clients.filter(c => !c.coachID).length; container.innerHTML = `

Clients

Client Code Current counselor
`; document.getElementById('coachSearch').addEventListener('input', (e) => { filterCoachSearch = e.target.value; renderCoachList(); }); document.getElementById('coachList').addEventListener('click', (e) => { const item = e.target.closest('.coach-list-item'); if (!item || item.dataset.action === 'clear-filter') return; document.querySelectorAll('.coach-list-item').forEach(x => x.classList.remove('active')); item.classList.add('active'); selectedCoachID = item.dataset.id; document.getElementById('filterByCoach').value = selectedCoachID; clientPage = 0; renderClientRows(); }); document.getElementById('filterByCoach').addEventListener('change', (e) => { const value = e.target.value; clientPage = 0; if (value && value !== '__unassigned__') { selectedCoachID = value; highlightCoachInList(value); } else { selectedCoachID = null; document.querySelectorAll('.coach-list-item').forEach(x => x.classList.remove('active')); } renderClientRows(); }); document.getElementById('clientSearch').addEventListener('input', () => { clientPage = 0; renderClientRows(); }); document.getElementById('selectAllBtn').addEventListener('click', () => { document.querySelectorAll('#clientTableBody .client-cb').forEach(cb => { cb.checked = true; selectedClientCodes.add(cb.value); }); updateSelectionInfo(); }); document.getElementById('clearSelBtn').addEventListener('click', () => { selectedClientCodes.clear(); document.querySelectorAll('#clientTableBody .client-cb').forEach(cb => { cb.checked = false; }); syncSelectAllCb(); updateSelectionInfo(); }); document.getElementById('selectAllCb').addEventListener('change', (e) => { document.querySelectorAll('#clientTableBody .client-cb').forEach(cb => { cb.checked = e.target.checked; if (e.target.checked) selectedClientCodes.add(cb.value); else selectedClientCodes.delete(cb.value); }); updateSelectionInfo(); }); document.getElementById('assignBtn').addEventListener('click', doAssign); renderCoachList(); renderClientRows(); } function getFilteredCoaches() { const q = filterCoachSearch.trim().toLowerCase(); if (!q) return coaches; return coaches.filter(c => { const hay = `${c.username} ${c.supervisorUsername || ''}`.toLowerCase(); return hay.includes(q); }); } function renderCoachList() { const list = document.getElementById('coachList'); if (!list) return; const filtered = getFilteredCoaches(); const q = filterCoachSearch.trim(); if (!filtered.length) { list.innerHTML = `
  • ${q ? 'No counselors match' : 'No counselors'}
  • `; return; } list.innerHTML = filtered.map(c => coachListItemHTML(c)).join(''); if (selectedCoachID) { highlightCoachInList(selectedCoachID); } } function coachListItemHTML(c) { const count = (clientsByCoach[c.coachID] || []).length; const active = selectedCoachID === c.coachID ? ' active' : ''; return `
  • ${esc(c.username)}
    ${c.supervisorUsername ? `
    ${esc(c.supervisorUsername)}
    ` : ''} ${count} client${count === 1 ? '' : 's'}
  • `; } function highlightCoachInList(coachID) { document.querySelectorAll('.coach-list-item').forEach(li => { li.classList.toggle('active', li.dataset.id === coachID); }); const active = document.querySelector(`.coach-list-item[data-id="${CSS.escape(coachID)}"]`); active?.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } function getVisibleClients() { const coachFilter = document.getElementById('filterByCoach')?.value ?? ''; const search = document.getElementById('clientSearch')?.value.trim().toLowerCase() ?? ''; let visible = clients; if (coachFilter === '__unassigned__') { visible = clients.filter(c => !c.coachID); } else if (coachFilter) { visible = clients.filter(c => c.coachID === coachFilter); } if (search) { visible = visible.filter(c => { const hay = `${c.clientCode} ${c.coachUsername || ''}`.toLowerCase(); return hay.includes(search); }); } return visible; } function updateClientSummary(filteredCount, pageSlice) { const el = document.getElementById('clientListSummary'); if (!el) return; const search = document.getElementById('clientSearch')?.value.trim() ?? ''; const coachFilter = document.getElementById('filterByCoach')?.value ?? ''; const hasFilter = search || coachFilter; const parts = []; if (hasFilter && filteredCount !== clients.length) { parts.push(`${filteredCount} of ${clients.length} match`); } else { parts.push(String(clients.length)); } if (pageSlice && filteredCount > CLIENT_PAGE_SIZE) { const from = clientPage * CLIENT_PAGE_SIZE + 1; const to = Math.min((clientPage + 1) * CLIENT_PAGE_SIZE, filteredCount); parts.push(`rows ${from}–${to}`); } el.textContent = `(${parts.join(', ')})`; } function renderPaginationBar(barId, totalRows, currentPage, pageSize, onPageChange) { const pag = document.getElementById(barId); if (!pag) return; if (!totalRows) { pag.innerHTML = ''; return; } const totalPages = Math.max(1, Math.ceil(totalRows / pageSize)); if (totalRows <= pageSize) { pag.innerHTML = `${totalRows} row${totalRows === 1 ? '' : 's'}`; return; } const from = currentPage * pageSize + 1; const to = Math.min((currentPage + 1) * pageSize, totalRows); const prevId = `${barId}Prev`; const nextId = `${barId}Next`; pag.innerHTML = ` Rows ${from}–${to} of ${totalRows} (page ${currentPage + 1}/${totalPages}) `; document.getElementById(prevId)?.addEventListener('click', () => onPageChange(currentPage - 1)); document.getElementById(nextId)?.addEventListener('click', () => onPageChange(currentPage + 1)); } function syncSelectAllCb(codesOnPage = []) { const allCb = document.getElementById('selectAllCb'); if (!allCb) return; if (!codesOnPage.length) { allCb.checked = false; allCb.indeterminate = false; return; } const allSelected = codesOnPage.every(code => selectedClientCodes.has(code)); const someSelected = codesOnPage.some(code => selectedClientCodes.has(code)); allCb.checked = allSelected; allCb.indeterminate = !allSelected && someSelected; } function renderClientRows() { const body = document.getElementById('clientTableBody'); if (!body) return; const visible = getVisibleClients(); const totalPages = Math.max(1, Math.ceil(visible.length / CLIENT_PAGE_SIZE)); if (clientPage >= totalPages) clientPage = totalPages - 1; if (!visible.length) { updateClientSummary(0, false); body.innerHTML = ` No clients match the current filters `; renderPaginationBar('clientPagination', 0, clientPage, CLIENT_PAGE_SIZE, (p) => { clientPage = p; renderClientRows(); }); syncSelectAllCb(); updateSelectionInfo(); return; } const slice = visible.slice(clientPage * CLIENT_PAGE_SIZE, (clientPage + 1) * CLIENT_PAGE_SIZE); updateClientSummary(visible.length, visible.length > CLIENT_PAGE_SIZE); body.innerHTML = slice.map(c => { const checked = selectedClientCodes.has(c.clientCode) ? ' checked' : ''; return ` ${esc(c.clientCode)} ${c.coachUsername ? esc(c.coachUsername) : 'Unassigned'} `; }).join(''); body.querySelectorAll('.client-cb').forEach(cb => { cb.addEventListener('change', () => { if (cb.checked) selectedClientCodes.add(cb.value); else selectedClientCodes.delete(cb.value); syncSelectAllCb(slice.map(c => c.clientCode)); updateSelectionInfo(); }); }); renderPaginationBar('clientPagination', visible.length, clientPage, CLIENT_PAGE_SIZE, (p) => { clientPage = p; renderClientRows(); }); syncSelectAllCb(slice.map(c => c.clientCode)); updateSelectionInfo(); } function getSelectedCodes() { return [...selectedClientCodes]; } function updateSelectionInfo() { const selected = getSelectedCodes(); const info = document.getElementById('selectionInfo'); const card = document.getElementById('assignActionCard'); const onPage = document.querySelectorAll('#clientTableBody .client-cb:checked').length; if (!selected.length) { info.style.display = 'none'; card.style.display = 'none'; return; } info.style.display = ''; const extra = selected.length > onPage ? ` (${onPage} on this page)` : ''; info.innerHTML = `${selected.length} client${selected.length === 1 ? '' : 's'} selected${extra}`; card.style.display = ''; if (selectedCoachID) { const sel = document.getElementById('targetCoachSelect'); if (sel) sel.value = selectedCoachID; } } async function doAssign() { const selected = getSelectedCodes(); const coachID = document.getElementById('targetCoachSelect').value; if (!selected.length) { showToast('Select at least one client', 'error'); return; } if (!coachID) { showToast('Select a target counselor', 'error'); return; } const coachName = coaches.find(c => c.coachID === coachID)?.username || coachID; if (!(await confirmAction({ title: 'Assign clients', message: `Assign ${selected.length} client(s) to ${coachName}?`, confirmLabel: 'Assign', }))) return; const btn = document.getElementById('assignBtn'); btn.disabled = true; btn.textContent = 'Assigning...'; try { const data = await apiPost('assignments.php', { clientCodes: selected, coachID }); if (data.success) { selected.forEach(code => { const c = clients.find(x => x.clientCode === code); if (c) { c.coachID = coachID; c.coachUsername = coachName; } }); selectedClientCodes.clear(); rebuildClientsByCoach(); showToast(`${data.assigned} client(s) assigned to ${coachName}`, 'success'); renderAssignments(); } } catch (e) { showToast(e.message, 'error'); btn.disabled = false; btn.textContent = 'Assign'; } } function esc(s) { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }