registerPage('employees', async (content, params = {}) => { document.getElementById('topbar-title').textContent = 'Employees'; let clients = []; async function loadClients() { const r = await api('clients.php', { action: 'list' }); if (r.success) clients = r.clients; } async function render(search = '', clientFilter = '') { const r = await api('employees.php', { action: 'list', search, clients_id: clientFilter }); if (!r.success) { document.getElementById('emp-table').innerHTML = emptyHTML('Error: ' + (r.error || 'unknown')); return; } if (r.prompt) { document.getElementById('emp-table').innerHTML = `

Select a client or enter a search term to load employees.

`; return; } const rows = r.employees; const limitNote = r.count >= r.limit ? `

Showing first ${r.limit} results — use search or client filter to narrow down.

` : ''; document.getElementById('emp-table').innerHTML = rows.length === 0 ? emptyHTML('No employees found') : limitNote + `
${rows.map(e => ``).join('')}
NameSurnameID/PassportClientOccupationCompany #Actions
${e.client_employees_name} ${e.surname} ${e.i_doc_passport || '—'} ${e.clients_name || '—'} ${e.occupation || '—'} ${e.company_number || '—'}
`; } content.innerHTML = `
${loadingHTML()}
`; await loadClients(); const clientSel = document.getElementById('ef-client'); const filterSel = document.getElementById('emp-client-filter'); clients.forEach(c => { clientSel.innerHTML += ``; filterSel.innerHTML += ``; }); const go = () => render(document.getElementById('emp-search').value, document.getElementById('emp-client-filter').value); document.getElementById('emp-search').addEventListener('input', go); document.getElementById('emp-client-filter').addEventListener('change', go); render(); function fillForm(e = null) { document.getElementById('emp-modal-title').textContent = e ? 'Edit Employee' : 'Add Employee'; document.getElementById('ef-id').value = e?.record_id || ''; document.getElementById('ef-name').value = e?.client_employees_name || ''; document.getElementById('ef-surname').value = e?.surname || ''; document.getElementById('ef-client').value = e?.clients_id || ''; document.getElementById('ef-id-doc').value = e?.i_doc_passport || ''; document.getElementById('ef-cell').value = e?.cell || ''; document.getElementById('ef-company-no').value = e?.company_number || ''; document.getElementById('ef-occupation').value = e?.occupation || ''; document.getElementById('ef-teba').value = e?.teba_number || ''; document.getElementById('ef-badge').value = e?.badge_number || ''; document.getElementById('ef-team').value = e?.team_number || ''; document.getElementById('ef-industry').value = e?.industry || ''; document.getElementById('ef-induction').value = e?.induction_date || ''; document.getElementById('ef-medical').value = e?.medical_date || ''; } window.empAdd = () => { const w = document.getElementById('emp-dup-warning'); if (w) w.remove(); document.getElementById('emp-results-section').style.display = 'none'; fillForm(); openModal('emp-modal'); }; window.empEdit = async (id) => { const w = document.getElementById('emp-dup-warning'); if (w) w.remove(); console.log('[employees] edit id:', id); const r = await api('employees.php', { action: 'get', id }); console.log('[employees] get response:', r); if (!r.success) { toast('Error: ' + r.error, 'error'); return; } fillForm(r.employee); // Show results section document.getElementById('emp-results-section').style.display = 'block'; document.getElementById('emp-results-list').innerHTML = loadingHTML(); openModal('emp-modal'); // Load results async loadEmployeeResults(id); }; async function loadEmployeeResults(empId) { const r = await api('results.php', { action: 'list', client_employees_id: empId }); if (!r.success) { document.getElementById('emp-results-list').innerHTML = emptyHTML('Failed to load results'); return; } const { assessments, tests } = r; if (!assessments.length && !tests.length) { document.getElementById('emp-results-list').innerHTML = `

No assessment or test results found.

`; return; } const assRows = assessments.map(a => { const passed = a.results === 'COMPETENT' || a.results === 'C'; return ` ${a.assesses_name || '—'} ${a.results || '—'} ${a.current_mark || '—'} / ${a.passmark || '—'} ${a.date || '—'} ${a.assessor_name || a.assessor_username || '—'} ${a.booking_number ? `` : ''} `; }).join(''); const testRows = tests.map(t => { const passed = t.results === 'COMPETENT' || t.results === 'C'; return ` ${t.test_name || '—'} ${t.results || '—'} ${t.current_mark || '—'} / ${t.passmark || '—'} ${t.date || '—'} ${t.assessor_name || t.assessor_username || '—'} ${t.booking_number ? `` : ''} `; }).join(''); const thead = `NameResultMarkDateAssessorActions`; let html = ''; if (assessments.length) { html += `

Assessments (${assessments.length})

${thead}${assRows}
`; } if (tests.length) { html += `

Tests (${tests.length})

${thead}${testRows}
`; } document.getElementById('emp-results-list').innerHTML = html; } // ── View Assessment popup ────────────────────────────────────────────────── window.viewAssessment = async (id) => { const r = await api('results.php', { action: 'get_assessment', id }); if (!r.success) { toast('Failed to load', 'error'); return; } const a = r.assessment; document.getElementById('result-view-title').textContent = a.assesses_name || 'Assessment'; const passed = a.results === 'COMPETENT' || a.results === 'C'; const sectionsHtml = (a.sections || []).map(sec => { const parts = (sec.subtotal || '').split('-'); const total = parts[0] || '?', score = parts[1] || '?'; const qRows = (sec.questions || []).map(q => ` ${q.question} ${+q.answer === 1 ? 'C' : 'NYC'} ${q.comment || ''} `).join(''); return `
${sec.section_name} ${score} / ${total}
${qRows}
Question Result Comment
`; }).join(''); document.getElementById('result-view-body').innerHTML = `

${a.assesses_name || '—'}

${a.results || '—'}

${a.client_employees_name || ''} ${a.surname || ''}

${a.i_doc_passport || '—'}

${a.company_number || '—'}

${a.clients_name || '—'}

${a.assessor_name || a.assessor_username || '—'}

${a.assessor_number || '—'}

${a.date || '—'}

${a.current_mark || '—'} / ${a.passmark || '—'}

${a.nqf_level || '—'}

${a.credits || '—'}

${a.vehicle_model ? `

${a.vehicle_model}

` : ''} ${a.notes ? `

${a.notes}

` : ''}

Sections & Questions

${sectionsHtml || '

No section data.

'}`; document.getElementById('result-view-footer').innerHTML = ` ${a.booking ? `` : ''} `; openModal('result-view-modal'); }; // ── View Test popup ──────────────────────────────────────────────────────── window.viewTest = async (id) => { const r = await api('results.php', { action: 'get_test', id }); if (!r.success) { toast('Failed to load', 'error'); return; } const t = r.test; document.getElementById('result-view-title').textContent = t.test_name || 'Test'; const passed = t.results === 'COMPETENT' || t.results === 'C'; const qHtml = (t.questions || []).map(q => { const parts = (q.subtotal || '').split('-'); const total = parts[0] || '?', score = parts[1] || '?'; const ansRows = (q.answers || []).map(a => ` ${a.answer} ${+a.option === 1 ? '✓' : '✗'} ${a.comment || ''} `).join(''); return `
${q.questions} ${score} / ${total}
${ansRows}
Option Selected Comment
`; }).join(''); document.getElementById('result-view-body').innerHTML = `

${t.test_name || '—'}

${t.results || '—'}

${t.client_employees_name || ''} ${t.surname || ''}

${t.i_doc_passport || '—'}

${t.clients_name || '—'}

${t.assessor_name || t.assessor_username || '—'}

${t.date || '—'}

${t.current_mark || '—'} / ${t.passmark || '—'}

Questions & Answers

${qHtml || '

No question data.

'}`; document.getElementById('result-view-footer').innerHTML = ` ${t.booking ? `` : ''} `; openModal('result-view-modal'); }; // ── Assessment PDF — matches old system layout ───────────────────────────── window.pdfAssessment = async (id) => { const r = await api('results.php', { action: 'get_assessment', id }); if (!r.success) { toast('Failed', 'error'); return; } const a = r.assessment; const passed = a.results === 'COMPETENT' || a.results === 'C'; const sigBase = window.location.origin + window.location.pathname.replace('index.php', '').replace(/\/[^/]*$/, '/'); const sectionsHtml = (a.sections || []).map(sec => { const parts = (sec.subtotal || '0-0').split('-'); const total = parts[0] || '0', score = parts[1] || '0'; const qRows = (sec.questions || []).map(q => `

${q.question}

${+q.answer === 1 ? 'C' : ''} ${+q.answer === 0 ? 'NYC' : ''} ${q.comment || ''} `).join(''); return ` ${qRows}

${sec.section_name}    Score: ${score}/${total}

CRITERIA C NYC COMMENT
`; }).join(''); const html = ` ${a.assesses_name}

SAFETY & TRAINING PRACTITIONERS

${a.assesses_info || ''}

NQF LEVEL: ${a.nqf_level || ''}

Credits: ${a.credits || ''}

${a.assesses_name || ''}

Vehicle Model: ${a.vehicle_model || ''}



CANDIDATE NAME

${a.client_employees_name || ''} ${a.surname || ''}

COY NO

${a.company_number || ''}

ID NO.

${a.i_doc_passport || ''}

MINE

${a.clients_name || ''}

ASSESSOR

${a.assessor_name || a.assessor_username || ''}

ASSESSOR NO

${a.assessor_number || ''}

ASSESSMENT DATE

${a.date || ''}

COMPETENT / NYC

${a.results || ''}

CAND. SIGNATURE

ASSESSOR SIGNATURE

DATE

${a.date || ''}



SAFETY & TRAINING PRACTITIONERS

${a.assesses_info || ''}

NQF LEVEL: ${a.nqf_level || ''}

Credits: ${a.credits || ''}

${a.assesses_name || ''}



${sectionsHtml}

I fully understand the above and was under no pressure or obligation to sign this document.
Signed on ${a.date || new Date().toLocaleDateString('en-ZA')}

Candidate Signature.

Assessor Signature.

CONFIRMATION OF TRAINING

I, ${a.client_employees_name || ''} ${a.surname || ''}, hereby confirm that I have received training in theory and practical on ${a.assesses_name || ''}. I confirm that I have been given the opportunity to ask questions and that answers given have been satisfactory. I have signed every section of this training evaluation. I furthermore undertake to carry out my appointed duties applicable to this equipment in a safe manner and to adhere to all safety aspects as required by Codes of Practice and Safe Working Procedures.

Signature of Candidate

Signature of Assessor;

Date:

${a.date || ''}

${a.image_file_path ? `

Photo of Candidate:

` : ''}