Online Employee Recognition & Rewards System

Employee Recognition & Rewards System

Welcome, User!

Here's a summary of your recognition activity. Keep up the great work!

Your Current Points Balance

0

Recent Company Recognitions

Recognize a Colleague

Acknowledge your peers for their hard work and positive contributions.

Redeem Your Points

You've earned it! Choose from the rewards below.

My Recognition & Redemption History

Recognition Received

DateFromCategoryMessagePoints

Rewards Redeemed

DateRewardCost

System Configuration

Manage employees, rewards, and recognition categories. Changes are applied in real-time.

Manage Employees

IDNamePointsActions

Manage Rewards

IDNameCost ($)Actions

Manage Recognition Categories

IDNamePointsActions
×

No recent recognitions yet.

'; return; } recentLogs.forEach(log => { const giver = this.findEmployeeById(log.from); const receiver = this.findEmployeeById(log.to); const category = this.findCategoryById(log.categoryId); if (!giver || !receiver || !category) return; const item = document.createElement('div'); item.className = 'ers-recognition-item'; item.innerHTML = `
${receiver.name.charAt(0)}
${giver.name} recognized ${receiver.name}

"${log.message || 'For ' + category.name}"

`; feed.appendChild(item); }); }, renderRecognitionForm() { const employeeSelect = this.elements.selectEmployee; const categorySelect = this.elements.selectCategory; if (!employeeSelect || !categorySelect) return; employeeSelect.innerHTML = ''; this.state.employees.forEach(emp => { if (emp.id !== this.state.currentUser.id) { employeeSelect.innerHTML += ``; } }); categorySelect.innerHTML = ''; this.state.recognitionCategories.forEach(cat => { categorySelect.innerHTML += ``; }); this.updateRecognitionPoints(); }, updateRecognitionPoints() { const categoryId = this.elements.selectCategory?.value; if (categoryId) { const category = this.findCategoryById(parseInt(categoryId)); if (category) { this.elements.recognitionPoints.value = category.points; } } else { this.elements.recognitionPoints.value = ''; } }, renderRewards() { const catalog = this.elements.rewardsCatalog; if (!catalog) return; catalog.innerHTML = ''; this.state.rewards.forEach(reward => { const canAfford = this.state.currentUser.points >= reward.cost; const card = document.createElement('div'); card.className = 'ers-reward-card'; card.innerHTML = `

${reward.name}

${reward.cost} PTS
`; catalog.appendChild(card); }); }, renderHistory() { const recTable = this.elements.recognitionHistoryTable; const redTable = this.elements.redemptionHistoryTable; if (!recTable || !redTable) return; recTable.innerHTML = ''; this.state.recognitionLog .filter(log => log.to === this.state.currentUser.id) .forEach(log => { const giver = this.findEmployeeById(log.from); const category = this.findCategoryById(log.categoryId); if (!giver || !category) return; recTable.innerHTML += ` ${log.date} ${giver.name} ${category.name} ${log.message} +${category.points} `; }); redTable.innerHTML = ''; this.state.redemptionLog .filter(log => log.userId === this.state.currentUser.id) .forEach(log => { const reward = this.findRewardById(log.rewardId); if (!reward) return; redTable.innerHTML += ` ${log.date} ${reward.name} -${reward.cost} `; }); }, renderAdminTables() { const empTable = this.elements.admin.employeesTable; const rewTable = this.elements.admin.rewardsTable; const catTable = this.elements.admin.categoriesTable; if (!empTable || !rewTable || !catTable) return; empTable.innerHTML = ''; this.state.employees.forEach(e => { empTable.innerHTML += ` ${e.id} ${e.name} ${e.points} `; }); rewTable.innerHTML = ''; this.state.rewards.forEach(r => { rewTable.innerHTML += ` ${r.id} ${r.name} $${r.cost.toFixed(2)} `; }); catTable.innerHTML = ''; this.state.recognitionCategories.forEach(c => { catTable.innerHTML += ` ${c.id} ${c.name} ${c.points} `; }); }, // --- EVENT HANDLERS & ACTIONS --- changeTab(tabIndex) { if (tabIndex < 0 || tabIndex >= this.elements.tabButtons.length) return; this.state.currentTab = tabIndex; this.elements.tabButtons.forEach((btn, index) => { btn.classList.toggle('active', index === tabIndex); }); this.elements.tabContents.forEach((content, index) => { content.classList.toggle('active', index === tabIndex); }); this.updateNavButtons(); }, updateNavButtons() { if (!this.elements.prevTabBtn || !this.elements.nextTabBtn) return; this.elements.prevTabBtn.style.visibility = this.state.currentTab === 0 ? 'hidden' : 'visible'; this.elements.nextTabBtn.style.visibility = this.state.currentTab === this.elements.tabButtons.length - 1 ? 'hidden' : 'visible'; }, handleRecognitionSubmit(e) { e.preventDefault(); const form = this.elements.recognitionForm; if (!form) return; const employeeId = parseInt(form.querySelector('#select-employee').value); const categoryId = parseInt(form.querySelector('#select-category').value); const message = form.querySelector('#recognition-message').value; if (!employeeId || !categoryId) { this.showNotification('Please select an employee and a category.', 'error'); return; } const category = this.findCategoryById(categoryId); const recipient = this.findEmployeeById(employeeId); if (!category || !recipient) return; // Update state recipient.points += category.points; this.state.recognitionLog.push({ from: this.state.currentUser.id, to: employeeId, categoryId: categoryId, message: message, date: new Date().toISOString().split('T')[0] }); this.showNotification(`Successfully recognized ${recipient.name}!`, 'success'); form.reset(); this.renderAll(); this.changeTab(0); // Switch to dashboard after submission }, handleRedeemReward(e) { if (e.target.matches('[data-reward-id]')) { const rewardId = parseInt(e.target.dataset.rewardId); const reward = this.findRewardById(rewardId); if (!reward) return; if (this.state.currentUser.points >= reward.cost) { this.state.currentUser.points -= reward.cost; this.state.redemptionLog.push({ userId: this.state.currentUser.id, rewardId: rewardId, date: new Date().toISOString().split('T')[0] }); this.showNotification(`You have redeemed "${reward.name}"!`, 'success'); this.renderAll(); } else { this.showNotification('You do not have enough points for this reward.', 'error'); } } }, downloadHistoryPDF() { // Guard clause for PDF library if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { this.showNotification('PDF generation is currently unavailable.', 'error'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const userName = this.state.currentUser.name; const date = new Date().toLocaleDateString(); // PDF Header doc.setFontSize(18); doc.text(`Recognition & Redemption History for ${userName}`, 14, 22); doc.setFontSize(11); doc.setTextColor(100); doc.text(`Report generated on: ${date}`, 14, 30); // Recognition Received Table const recData = this.state.recognitionLog .filter(log => log.to === this.state.currentUser.id) .map(log => { const giver = this.findEmployeeById(log.from); const category = this.findCategoryById(log.categoryId); return [log.date, giver.name, category.name, `+${category.points}`, log.message]; }); doc.autoTable({ startY: 40, head: [['Date', 'From', 'Category', 'Points', 'Message']], body: recData, theme: 'striped', headStyles: { fillColor: [79, 70, 229] } // --primary-color }); // Rewards Redeemed Table const redData = this.state.redemptionLog .filter(log => log.userId === this.state.currentUser.id) .map(log => { const reward = this.findRewardById(log.rewardId); return [log.date, reward.name, `-${reward.cost}`]; }); if(redData.length > 0) { doc.autoTable({ startY: doc.autoTable.previous.finalY + 15, head: [['Date', 'Reward Redeemed', 'Point Cost']], body: redData, theme: 'striped', headStyles: { fillColor: [79, 70, 229] } }); } doc.save(`${userName}_Recognition_History.pdf`); this.showNotification('PDF download started.', 'success'); }, // --- ADMIN MODAL & CRUD LOGIC --- openModal(type, id = null) { const form = this.elements.modalForm; const fieldsContainer = this.elements.modalFields; const title = this.elements.modalTitle; const editIdInput = this.elements.modalEditId; if (!form || !fieldsContainer || !title || !editIdInput) return; form.reset(); form.dataset.type = type; editIdInput.value = id || ''; let fieldsHTML = ''; let item = null; switch (type) { case 'employee': title.textContent = id ? 'Edit Employee' : 'Add Employee'; item = id ? this.findEmployeeById(id) : {}; fieldsHTML = `
`; break; case 'reward': title.textContent = id ? 'Edit Reward' : 'Add Reward'; item = id ? this.findRewardById(id) : {}; fieldsHTML = `
`; break; case 'category': title.textContent = id ? 'Edit Category' : 'Add Category'; item = id ? this.findCategoryById(id) : {}; fieldsHTML = `
`; break; } fieldsContainer.innerHTML = fieldsHTML; this.elements.modal.style.display = 'flex'; }, closeModal() { if (this.elements.modal) this.elements.modal.style.display = 'none'; }, editItem(type, id) { this.openModal(type, id); }, deleteItem(type, id) { if (confirm(`Are you sure you want to delete this ${type}? This action cannot be undone.`)) { let dataArray; switch (type) { case 'employee': dataArray = this.state.employees; break; case 'reward': dataArray = this.state.rewards; break; case 'category': dataArray = this.state.recognitionCategories; break; } const index = dataArray.findIndex(item => item.id === id); if (index > -1) { dataArray.splice(index, 1); this.showNotification(`${type.charAt(0).toUpperCase() + type.slice(1)} deleted.`, 'success'); this.renderAll(); } } }, handleModalFormSubmit(e) { e.preventDefault(); const type = e.target.dataset.type; const id = parseInt(this.elements.modalEditId.value); let dataArray, newItem; const name = document.getElementById('modal-name').value; switch (type) { case 'employee': dataArray = this.state.employees; const points = parseInt(document.getElementById('modal-points').value); newItem = { id: id || this.getNextId(dataArray), name, points }; break; case 'reward': dataArray = this.state.rewards; const cost = parseInt(document.getElementById('modal-cost').value); newItem = { id: id || this.getNextId(dataArray), name, cost }; break; case 'category': dataArray = this.state.recognitionCategories; const catPoints = parseInt(document.getElementById('modal-points').value); newItem = { id: id || this.getNextId(dataArray), name, points: catPoints }; break; } if (id) { // Editing existing const index = dataArray.findIndex(item => item.id === id); if (index > -1) dataArray[index] = newItem; } else { // Adding new dataArray.push(newItem); } this.showNotification(`${type.charAt(0).toUpperCase() + type.slice(1)} saved successfully.`, 'success'); this.closeModal(); this.renderAll(); }, // --- UTILITY FUNCTIONS --- findEmployeeById(id) { return this.state.employees.find(e => e.id === id); }, findCategoryById(id) { return this.state.recognitionCategories.find(c => c.id === id); }, findRewardById(id) { return this.state.rewards.find(r => r.id === id); }, getNextId(array) { return array.length > 0 ? Math.max(...array.map(item => item.id)) + 1 : 1; }, showNotification(message, type = 'success') { const box = this.elements.notificationBox; if (!box) return; box.textContent = message; box.className = `ers-notification ${type}`; box.classList.add('show'); setTimeout(() => { box.classList.remove('show'); }, 3000); } }; // Make ERS object globally accessible for inline onclick handlers window.ERS = ERS; // Start the application ERS.init(); });
Scroll to Top