🎓
IACP Training & Development
Human Resources — Evarist Byamaka
?
← Portal ← Dashboard
Portal ERP | HR Finance Production Quality Stores Procurement Infra Sales Office Agri Tourism Export RDP POW Analytics Inventory Mail Training
🎓
DepartmentLearning & Development
👤
Module ManagerEvarist Turyigurirwa
🏢
LocationRwashameire, Ntungamo
SystemChecking...
📅
Date
Time
📆
Fiscal YearFY 2025/2026
Training Programs
All approved training programs for IACP staff
#TitleCategoryTrainer / Provider ModeDuration (hrs)Max Participants Cost/Person (UGX)StatusActions
Training Sessions
Scheduled, ongoing and completed sessions
#ProgramStart DateEnd Date VenueStatusEnrolled / CapTotal Cost (UGX)Actions
Enrollments
Staff enrollment, attendance and results
#EmployeeDepartmentProgram Session DatesStatusScoreCertificateActions
Competency Framework
Required competency levels by department and role
#DepartmentRole / PositionCompetencyRequired LevelDescriptionActions
Employee Competency Assessments
Current levels vs required — gap analysis
Gap Analysis — employees highlighted in red have a current level below the required level. Sort by gap to prioritise training.
#EmployeeCompetencyCurrent LevelRequired LevelGapLast AssessedAssessed By
Certificates Issued
All certificates awarded to IACP staff
Training & Development — User Guide

Programs Tab

  1. Click Add Program to register a new training program. Fill in the title, category, trainer name, provider type, delivery mode, duration, max participants and cost per person.
  2. Use the Category, Mode and Status filters to narrow the programs list.
  3. Click Edit on any row to update program details. Changes are saved to localStorage immediately.
  4. Click Archive to mark a program inactive — it will no longer appear when scheduling new sessions.
  5. Click Delete to permanently remove a program. This cannot be undone.
  6. Click Print List to generate a print-ready report of all programs with IACP branding.

Sessions Tab

  1. Click Schedule Session to create a new training session. Select the program, set start and end dates, enter the venue, and set the initial status.
  2. Sessions happening within the next 14 days appear in the Upcoming Sessions banner at the top of the tab.
  3. Click View Participants to see all staff enrolled in a session.
  4. Use the Complete action to mark a session as completed after it has run.
  5. Use Cancel to mark a session as cancelled. Enrolled participants are not automatically notified — communicate changes separately.

Enrollments Tab

  1. Click Enroll Employee to enrol a single staff member into a session. Select the employee from the IACP staff list, then select the session.
  2. Use Bulk Enroll Dept to automatically enrol all staff in a department into a chosen session at once.
  3. Click Attend / Absent buttons to record attendance for each enrollee after the session runs.
  4. Click Enter Score to record an assessment score (0–100). The pass mark is 70.
  5. Click Issue Certificate on any row where the employee attended (and passed if scored). A certificate record is created in the Certificates tab with a unique reference number.
  6. Use the Department, Status and Date Range filters to focus the view.

Competency Tab

  1. The Competency Framework table defines the required competency level (1–5) for each role in each department. Add entries with Add Competency.
  2. Click Assess Employee to record a staff member's current competency level against a defined competency. Enter the assessor's name and the current level (1–5).
  3. The Gap column highlights the difference between current and required level. Negative gaps (shown in red) indicate training needs.
  4. Use the Department filter to focus on a specific department's gap analysis.

Certificates Tab

  1. All issued certificates appear here in card format. Click any card to open the printable certificate.
  2. Click Print Certificate inside the certificate viewer to generate a formal, branded, print-ready certificate with IACP letterhead, employee name, program, date and signature line.
  3. Certificate numbers follow the format IACP/CERT/YYYY/NNN and are auto-generated.
  4. Use the search bar to find certificates by employee or program name. Filter by year or validity status.
  5. Click Download All (Print List) to print a full register of all certificates for record-keeping.
Note: All data is stored in the browser localStorage. For permanent storage, ensure the backend server is running and the module is connected to the /api/training endpoint.
`); w.document.close(); w.print(); } /* ═══════════════════════════════════════════ TAB 2 — SESSIONS ═══════════════════════════════════════════ */ function renderSessions() { renderUpcoming(); const sess = sessions(); const progs = programs(); const enrs = enrollments(); const tbody = document.getElementById('sessionsTbody'); if (!sess.length) { tbody.innerHTML='No sessions scheduled.'; return; } tbody.innerHTML = sess.map((s,i)=>{ const prog = progs.find(p=>p.id===s.programId)||{title:'Unknown',maxPax:0,cost:0}; const enrolled = enrs.filter(e=>e.sessionId===s.id).length; const totalCost = enrolled * (prog.cost||0); return ` ${i+1} ${prog.title} ${s.start}${s.end} ${s.venue} ${badgeStatus(s.status)} ${enrolled} / ${prog.maxPax} ${totalCost===0?'—':'UGX '+fmt(totalCost)} ${s.status!=='Completed'?``:''} ${s.status!=='Cancelled'&&s.status!=='Completed'?``:''} `; }).join(''); } function renderUpcoming() { const today = new Date(); today.setHours(0,0,0,0); const limit = new Date(today); limit.setDate(limit.getDate()+14); const progs = programs(); const upcoming = sessions().filter(s=>{ const d = new Date(s.start); return d >= today && d <= limit && s.status==='Scheduled'; }); const strip = document.getElementById('upcomingSessions'); const list = document.getElementById('upcomingList'); if (!upcoming.length) { strip.style.display='none'; return; } strip.style.display='block'; list.innerHTML = upcoming.map(s=>{ const prog = progs.find(p=>p.id===s.programId)||{title:'Unknown'}; return `
${s.start}
${prog.title}
${s.venue}
${badgeStatus(s.status)}
`; }).join(''); } function populateSessionDropdown(selectId) { const sel = document.getElementById(selectId); const progs = programs(); sel.innerHTML = sessions() .filter(s=>s.status==='Scheduled'||s.status==='Ongoing') .map(s=>{ const prog = progs.find(p=>p.id===s.programId)||{title:'Unknown'}; return ``; }).join(''); } async function saveSession() { const id = document.getElementById('editSessionId').value; const progId = document.getElementById('sessProgram').value; const start = document.getElementById('sessStart').value; const end = document.getElementById('sessEnd').value; const venue = document.getElementById('sessVenue').value.trim(); const status = document.getElementById('sessStatus').value; if (!progId||!start||!end||!venue) { alert('All fields are required.'); return; } const sess = {id: id||uid('s'), programId:progId, start, end, venue, status}; try { if (window.api && api.isOnline() !== false) await api.training.createSession(sess); } catch(e) {} let list = sessions(); if (id) { const idx=list.findIndex(s=>s.id===id); if(idx>-1) list[idx]=sess; } else list.push(sess); saveSessions(list); closeModal('modalScheduleSession'); renderSessions(); } function viewParticipants(sessId) { const progs = programs(); const sess = sessions().find(s=>s.id===sessId)||{}; const prog = progs.find(p=>p.id===sess.programId)||{title:'Session'}; const enrs = enrollments().filter(e=>e.sessionId===sessId); document.getElementById('vpTitle').textContent = prog.title+' — Participants'; if (!enrs.length) { document.getElementById('vpContent').innerHTML='
No participants enrolled yet.
'; } else { document.getElementById('vpContent').innerHTML=` ${enrs.map((e,i)=>``).join('')}
#EmployeeDepartmentStatusScore
${i+1}${e.employee}${e.dept} ${badgeStatus(e.status)} ${e.score!=null?e.score+'%':'—'}
`; } openModal('modalViewParticipants'); } async function completeSession(id) { if (!confirm('Mark this session as Completed?')) return; const updated = sessions().map(s=>s.id===id?{...s,status:'Completed'}:s); const sess = updated.find(s=>s.id===id); try { if (window.api && api.isOnline() !== false && sess) await api.training.createSession(sess); } catch(e) {} saveSessions(updated); renderSessions(); } async function cancelSession(id) { if (!confirm('Cancel this session?')) return; const updated = sessions().map(s=>s.id===id?{...s,status:'Cancelled'}:s); const sess = updated.find(s=>s.id===id); try { if (window.api && api.isOnline() !== false && sess) await api.training.createSession(sess); } catch(e) {} saveSessions(updated); renderSessions(); } /* Open Schedule Session modal — populate program dropdown */ document.getElementById('modalScheduleSession').addEventListener('click', function(){}); (function patchSessionModal(){ const orig = openModal; window.openModal = function(id) { if (id==='modalScheduleSession') { const progs = programs().filter(p=>p.status==='Active'); document.getElementById('sessProgram').innerHTML = progs.map(p=>``).join(''); document.getElementById('editSessionId').value = ''; } if (id==='modalEnrollEmployee') { document.getElementById('enrEmployee').innerHTML = STAFF_LIST.map(s=>``).join(''); populateSessionDropdown('enrSession'); } if (id==='modalBulkEnroll') { populateSessionDropdown('bulkSession'); } if (id==='modalAssessEmployee') { document.getElementById('assessEmployee').innerHTML = STAFF_LIST.map(s=>``).join(''); document.getElementById('assessCompetency').innerHTML = competencies().map(c=>``).join(''); document.getElementById('assessDate').value = new Date().toISOString().split('T')[0]; document.getElementById('editAssessId').value = ''; } orig(id); }; })(); /* ═══════════════════════════════════════════ TAB 3 — ENROLLMENTS ═══════════════════════════════════════════ */ function renderEnrollments() { const dept = document.getElementById('fEnrDept').value; const status = document.getElementById('fEnrStatus').value; const from = document.getElementById('fEnrFrom').value; const to = document.getElementById('fEnrTo').value; const sess = sessions(); let list = enrollments(); if (dept) list = list.filter(e=>e.dept===dept); if (status) list = list.filter(e=>e.status===status); if (from||to) { list = list.filter(e=>{ const s = sess.find(x=>x.id===e.sessionId); if (!s) return false; const d = s.start; if (from && d < from) return false; if (to && d > to) return false; return true; }); } const progs = programs(); const tbody = document.getElementById('enrollmentsTbody'); if (!list.length) { tbody.innerHTML='No enrollment records found.'; return; } tbody.innerHTML = list.map((e,i)=>{ const sess_ = sess.find(s=>s.id===e.sessionId)||{start:'',end:''}; const prog = progs.find(p=>p.id===sess_.programId)||{title:'Unknown'}; const scoreBar = e.score!=null ? `
${e.score}%
` : ''; const canIssueCert = (e.status==='Attended') && !e.certIssued && (e.score==null || e.score>=70); return ` ${i+1} ${e.employee} ${e.dept} ${prog.title} ${sess_.start}${sess_.end&&sess_.end!==sess_.start?' – '+sess_.end:''} ${badgeStatus(e.status)} ${scoreBar} ${e.certIssued?'Issued':''} ${e.status==='Enrolled'?` `:''} ${e.status==='Attended'?``:''} ${canIssueCert?``:''} `; }).join(''); } async function saveEnrollment() { const empVal = document.getElementById('enrEmployee').value; const sessId = document.getElementById('enrSession').value; if (!empVal||!sessId) { alert('Select employee and session.'); return; } const [name,dept] = empVal.split('|'); // Check for duplicate const existing = enrollments().find(e=>e.employee===name&&e.sessionId===sessId); if (existing) { alert(name+' is already enrolled in this session.'); return; } const enr = {id:uid('e'), employee:name, dept, sessionId:sessId, status:'Enrolled', score:null, certIssued:false}; try { if (window.api && api.isOnline() !== false) await api.training.enroll(enr); } catch(e) {} const list = enrollments(); list.push(enr); saveEnrollments(list); closeModal('modalEnrollEmployee'); renderEnrollments(); renderProgramKpis(); } async function saveBulkEnroll() { const dept = document.getElementById('bulkDept').value; const sessId = document.getElementById('bulkSession').value; if (!dept||!sessId) { alert('Select department and session.'); return; } const staff = STAFF_LIST.filter(s=>s.dept===dept); if (!staff.length) { alert('No staff found for this department.'); return; } let list = enrollments(); const newEnrs = []; staff.forEach(s=>{ if (!list.find(e=>e.employee===s.name&&e.sessionId===sessId)) { newEnrs.push({id:uid('e'), employee:s.name, dept:s.dept, sessionId:sessId, status:'Enrolled', score:null, certIssued:false}); } }); const added = newEnrs.length; if (added) { try { if (window.api && api.isOnline() !== false) await api.training.bulkEnroll({sessionId:sessId, enrollments:newEnrs}); } catch(e) {} newEnrs.forEach(e=>list.push(e)); saveEnrollments(list); } closeModal('modalBulkEnroll'); alert(`${added} staff enrolled from ${dept} (${staff.length-added} already enrolled).`); renderEnrollments(); renderProgramKpis(); } async function markAttendance(id, status) { try { if (window.api && api.isOnline() !== false) await api.training.complete(id, {status}); } catch(e) {} saveEnrollments(enrollments().map(e=>e.id===id?{...e,status}:e)); renderEnrollments(); } function openScoreModal(id, name) { document.getElementById('scoreEnrId').value = id; document.getElementById('scoreEnrName').textContent = 'Recording score for: '+name; document.getElementById('scoreValue').value = ''; openModal('modalScore'); } async function saveScore() { const id = document.getElementById('scoreEnrId').value; const score = parseInt(document.getElementById('scoreValue').value); if (isNaN(score)||score<0||score>100) { alert('Enter a score between 0 and 100.'); return; } try { if (window.api && api.isOnline() !== false) await api.training.complete(id, {score}); } catch(e) {} saveEnrollments(enrollments().map(e=>e.id===id?{...e,score}:e)); closeModal('modalScore'); renderEnrollments(); } async function issueCertificate(enrId) { const enr = enrollments().find(e=>e.id===enrId); if(!enr) return; const sess_ = sessions().find(s=>s.id===enr.sessionId)||{}; const prog = programs().find(p=>p.id===sess_.programId)||{title:'Training Program'}; const cert = { id:uid('cert'), employee:enr.employee, dept:enr.dept, program:prog.title, enrollmentId:enrId, issueDate:new Date().toISOString().split('T')[0], certNo:nextCertNo(), expiry:'', status:'Valid' }; try { if (window.api && api.isOnline() !== false) await api.training.issueCert(cert); } catch(e) {} const cList = certificates(); cList.push(cert); saveCertificates(cList); // Mark cert as issued on enrollment try { if (window.api && api.isOnline() !== false) await api.training.complete(enrId, {certIssued:true}); } catch(e) {} saveEnrollments(enrollments().map(e=>e.id===enrId?{...e,certIssued:true}:e)); alert('Certificate '+cert.certNo+' issued to '+enr.employee); renderEnrollments(); renderProgramKpis(); } /* ═══════════════════════════════════════════ TAB 4 — COMPETENCY ═══════════════════════════════════════════ */ function renderCompetency() { renderFramework(); renderAssessments(); } function renderFramework() { const deptF = document.getElementById('fCompDept').value; let list = competencies(); if (deptF) list = list.filter(c=>c.dept===deptF); const tbody = document.getElementById('frameworkTbody'); if (!list.length) { tbody.innerHTML='No competencies defined.'; return; } tbody.innerHTML = list.map((c,i)=>` ${i+1}${c.dept}${c.role}${c.name} ${levelPips(c.required,c.required)} Level ${c.required} ${c.description||'—'} `).join(''); } function renderAssessments() { const deptF = document.getElementById('fCompDept').value; let list = assessments().sort((a,b)=>{ const compA = competencies().find(c=>c.id===a.competencyId); const compB = competencies().find(c=>c.id===b.competencyId); const gapA = compA ? compA.required - a.current : 0; const gapB = compB ? compB.required - b.current : 0; return gapB - gapA; // largest gap first }); if (deptF) { const compIds = competencies().filter(c=>c.dept===deptF).map(c=>c.id); list = list.filter(a=>compIds.includes(a.competencyId)); } const tbody = document.getElementById('assessmentsTbody'); if (!list.length) { tbody.innerHTML='No assessments recorded.'; return; } tbody.innerHTML = list.map((a,i)=>{ const comp = competencies().find(c=>c.id===a.competencyId)||{name:'Unknown',required:0}; const gap = comp.required - a.current; const gapStyle = gap>0?'color:var(--red);font-weight:700':(gap<0?'color:var(--green);font-weight:700':'color:var(--text-muted)'); return `0?'style="background:var(--pale-red)"':''}> ${i+1}${a.employee} ${comp.name} ${levelPips(a.current,comp.required)} Level ${a.current} ${levelPips(comp.required,comp.required)} Level ${comp.required} ${gap>0?'+'+gap+' gap':(gap<0?Math.abs(gap)+' above':'On target')} ${a.date||'—'} ${a.assessor||'—'} `; }).join(''); } function saveCompetency() { const id = document.getElementById('editCompId').value; const dept = document.getElementById('compDept').value; const role = document.getElementById('compRole').value.trim(); const name = document.getElementById('compName').value.trim(); const req = parseInt(document.getElementById('compRequired').value); if (!dept||!role||!name) { alert('Department, role and name are required.'); return; } const comp = {id:id||uid('c'), dept, role, name, required:req, description:document.getElementById('compDescription').value.trim()}; let list = competencies(); if (id) { const idx=list.findIndex(c=>c.id===id); if(idx>-1) list[idx]=comp; } else list.push(comp); saveCompetencies(list); closeModal('modalAddCompetency'); renderCompetency(); } function editCompetency(id) { const c = competencies().find(x=>x.id===id); if(!c) return; document.getElementById('editCompId').value = c.id; document.getElementById('compDept').value = c.dept; document.getElementById('compRole').value = c.role; document.getElementById('compName').value = c.name; document.getElementById('compRequired').value = c.required; document.getElementById('compDescription').value = c.description||''; document.getElementById('modalCompTitle').textContent = 'Edit Competency'; openModal('modalAddCompetency'); } function deleteCompetency(id) { if (!confirm('Delete this competency framework entry?')) return; saveCompetencies(competencies().filter(c=>c.id!==id)); renderCompetency(); } function saveAssessment() { const id = document.getElementById('editAssessId').value; const employee = document.getElementById('assessEmployee').value; const compId = document.getElementById('assessCompetency').value; const current = parseInt(document.getElementById('assessLevel').value); const assessor = document.getElementById('assessorName').value.trim(); const date = document.getElementById('assessDate').value; if (!employee||!compId) { alert('Employee and competency are required.'); return; } const assess = {id:id||uid('a'), employee, competencyId:compId, current, assessor, date}; let list = assessments(); if (id) { const idx=list.findIndex(a=>a.id===id); if(idx>-1) list[idx]=assess; } else list.push(assess); saveAssessments(list); closeModal('modalAssessEmployee'); renderCompetency(); } /* ═══════════════════════════════════════════ TAB 5 — CERTIFICATES ═══════════════════════════════════════════ */ function renderCertificates() { const search = (document.getElementById('fCertSearch').value||'').toLowerCase(); const year = document.getElementById('fCertYear').value; const status = document.getElementById('fCertStatus').value; let list = certificates(); if (search) list = list.filter(c=>c.employee.toLowerCase().includes(search)||c.program.toLowerCase().includes(search)); if (year) list = list.filter(c=>c.certNo.includes('/'+year+'/')); if (status) list = list.filter(c=>c.status===status); const gallery = document.getElementById('certGallery'); if (!list.length) { gallery.innerHTML='
No certificates found matching the filters.
'; return; } gallery.innerHTML = list.map(c=>`
🏅
${c.employee}
${c.program}
Dept: ${c.dept} Issued: ${c.issueDate} ${c.expiry?`Expires: ${c.expiry}`:''}
${c.certNo}
${badgeStatus(c.status)}
`).join(''); } function viewCertificate(id) { const c = certificates().find(x=>x.id===id); if(!c) return; document.getElementById('certPrintArea').innerHTML = buildCertHtml(c); openModal('modalCertView'); } function buildCertHtml(c) { const issued = new Date(c.issueDate).toLocaleDateString('en-GB',{day:'2-digit',month:'long',year:'numeric'}); return `
Inspire Africa Group
INSPIRE AFRICA COFFEE PARK
Rwashameire, Ntungamo District, Uganda | africacoffepark.com
This is to certify that
${c.employee}
has successfully completed the program
${c.program}
Department: ${c.dept}
Date of Issue: ${issued}
Certificate No: ${c.certNo}
${c.expiry?`
Valid Until: ${c.expiry}
`:''}
Status: ${c.status}
Ntungamo, Uganda
Evarist Byamaka
HR Manager — Training & Development
Nelson Tugume
Chief Executive Officer, Inspire Africa Group
Inspire Africa Coffee Park • Elevating Uganda Coffee to the World
`; } function printCertificate() { const content = document.getElementById('certDocument'); if (!content) return; const w = window.open('','_blank'); w.document.write(`Certificate ${content.outerHTML}