// Employee CRUD operations let currentPage = 1; let itemsPerPage = 20; let allEmployees = []; let employeesCollectionName = 'Employee_Records'; let currentView = localStorage.getItem('employeesView') || 'table'; // 'table' or 'card' // Common employee field names that we'll display (matching PocketBase schema) const standardFields = ['First_Name', 'Last_Name', 'Email_Address', 'Phone_Number', 'Voxer_ID', 'Status']; /** * Load all employees with optional search filter */ async function loadEmployees(page = 1) { try { // Show loading state based on current view if (currentView === 'table') { const tbody = document.getElementById('employeesTableBody'); if (tbody) { tbody.innerHTML = 'Loading...'; } } else { const container = document.getElementById('employeesCardContainer'); if (container) { container.innerHTML = '
Loading...
'; } } currentPage = page; const searchTerm = document.getElementById('searchInput')?.value || ''; // Build search filter let searchFilter = ''; if (searchTerm) { // Escape special characters for PocketBase filter const escapedTerm = searchTerm.replace(/"/g, '\\"'); // Search across common text fields searchFilter = `First_Name ~ "${escapedTerm}" || Last_Name ~ "${escapedTerm}" || Email_Address ~ "${escapedTerm}" || Phone_Number ~ "${escapedTerm}" || Voxer_ID ~ "${escapedTerm}" || Status ~ "${escapedTerm}"`; } const result = await pb.collection(employeesCollectionName).getList(page, itemsPerPage, { filter: searchFilter || undefined, sort: 'Last_Name', }); allEmployees = result.items; displayEmployees(allEmployees); displayPagination(result.page, result.totalPages, result.totalItems); // Update view based on current view preference updateViewDisplay(); } catch (error) { console.error('Error loading employees:', error); showMessage('Error loading employees: ' + (error.message || 'Unknown error'), 'error'); if (currentView === 'table') { const tbody = document.getElementById('employeesTableBody'); if (tbody) { tbody.innerHTML = 'Error loading employees. Please refresh the page.'; } } else { const container = document.getElementById('employeesCardContainer'); if (container) { container.innerHTML = '
Error loading employees. Please refresh the page.
'; } } } } /** * Display employees in table or card view */ function displayEmployees(employees) { if (currentView === 'table') { displayEmployeesTable(employees); } else { displayEmployeesCards(employees); } } /** * Display employees in table view */ function displayEmployeesTable(employees) { const tbody = document.getElementById('employeesTableBody'); if (!tbody) return; if (!employees || employees.length === 0) { tbody.innerHTML = 'No employees found'; return; } tbody.innerHTML = employees.map(employee => { // Use correct field names from schema const firstName = employee.First_Name || '-'; const lastName = employee.Last_Name || '-'; const email = employee.Email_Address || '-'; const phone = employee.Phone_Number || '-'; const voxerId = employee.Voxer_ID || '-'; const status = employee.Status || '-'; return ` ${escapeHtml(firstName)} ${escapeHtml(lastName)} ${escapeHtml(phone)} ${escapeHtml(email)} ${escapeHtml(voxerId)} ${escapeHtml(status)}
`; }).join(''); } /** * Display employees in card view */ function displayEmployeesCards(employees) { const container = document.getElementById('employeesCardContainer'); if (!container) return; if (!employees || employees.length === 0) { container.innerHTML = '
No employees found
'; return; } container.innerHTML = employees.map(employee => { // Use correct field names from schema const firstName = employee.First_Name || ''; const lastName = employee.Last_Name || ''; const fullName = `${firstName} ${lastName}`.trim() || 'N/A'; const email = employee.Email_Address || '-'; const phone = employee.Phone_Number || '-'; const voxerId = employee.Voxer_ID || '-'; const status = employee.Status || '-'; return `

${escapeHtml(fullName)}

${status !== '-' ? `
${escapeHtml(status)}
` : ''}
Email ${email !== '-' ? `

${escapeHtml(email)}

Send Email ` : '

-

'}
Phone ${phone !== '-' ? `

${escapeHtml(phone)}

Call ` : '

-

'}
Voxer ID

${escapeHtml(voxerId)}

`; }).join(''); } /** * Display pagination controls */ function displayPagination(currentPage, totalPages, totalItems) { const container = document.getElementById('paginationContainer'); if (!container) return; if (totalPages <= 1) { container.innerHTML = ''; return; } let html = '
'; // Previous button if (currentPage > 1) { html += ``; } // Page info html += `Page ${currentPage} of ${totalPages} (${totalItems} total)`; // Next button if (currentPage < totalPages) { html += ``; } html += '
'; container.innerHTML = html; } /** * Load employee for editing */ async function loadEmployeeForEdit(employeeId) { try { if (!employeeId) { showMessage('No employee ID provided', 'error'); return null; } const employee = await pb.collection(employeesCollectionName).getOne(employeeId); // Set current employee ID for reports window.currentEmployeeId = employeeId; // Update modal header if (typeof updateEmployeeModalInfo === 'function') { updateEmployeeModalInfo(employee); } // Show reports tab for existing employees - do this after a short delay to ensure DOM is ready setTimeout(() => { const reportsTabButton = document.getElementById('tab-reports'); if (reportsTabButton) { reportsTabButton.classList.remove('hidden'); } }, 50); // Populate standard form fields using correct field names const firstNameField = document.getElementById('First_Name'); if (firstNameField) firstNameField.value = employee.First_Name || ''; const lastNameField = document.getElementById('Last_Name'); if (lastNameField) lastNameField.value = employee.Last_Name || ''; const emailField = document.getElementById('Email_Address'); if (emailField) emailField.value = employee.Email_Address || ''; const phoneField = document.getElementById('Phone_Number'); if (phoneField) phoneField.value = employee.Phone_Number || ''; const voxerField = document.getElementById('Voxer_ID'); if (voxerField) voxerField.value = employee.Voxer_ID || ''; const statusField = document.getElementById('Status'); if (statusField) statusField.value = employee.Status || ''; // Handle additional fields dynamically const additionalFieldsContainer = document.getElementById('additionalFieldsContainer'); if (additionalFieldsContainer) { additionalFieldsContainer.innerHTML = ''; // Get all fields from the employee record const allFields = Object.keys(employee); const systemFields = new Set(['id', 'created', 'updated', 'collectionId', 'collectionName']); // Add any fields that aren't in the standard fields allFields.forEach(field => { // Skip system fields if (systemFields.has(field) || field.startsWith('@')) { return; } // Skip standard fields (already handled above) if (standardFields.includes(field)) { return; } // Create input for this additional field if (employee[field] !== undefined) { const fieldLabel = field.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); const fieldId = `field_${field}`; const fieldDiv = document.createElement('div'); fieldDiv.innerHTML = ` `; additionalFieldsContainer.appendChild(fieldDiv.firstElementChild); } }); } return employee; } catch (error) { console.error('Error loading employee:', error); showMessage('Error loading employee: ' + (error.message || 'Unknown error'), 'error'); return null; } } /** * Escape HTML to prevent XSS */ function escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, m => map[m]); } /** * Save employee (create or update) */ async function saveEmployee() { try { const form = document.getElementById('employeeForm'); if (!form) { showMessage('Form not found', 'error'); return; } const formData = new FormData(form); const employeeId = document.getElementById('employeeId')?.value; // Build employee object const employeeData = {}; // Get all form fields from FormData (this includes standard fields) for (const [key, value] of formData.entries()) { if (key === 'id') continue; // Include the value (even if empty for standard fields to allow clearing) if (value !== null && value !== undefined) { employeeData[key] = value.trim(); } } // Also get additional fields from dynamically created inputs const additionalFields = document.querySelectorAll('#additionalFieldsContainer input'); additionalFields.forEach(input => { if (input.name && input.value !== null && input.value !== undefined) { employeeData[input.name] = input.value.trim(); } }); if (employeeId) { // Update existing employee const updatedEmployee = await pb.collection(employeesCollectionName).update(employeeId, employeeData); const employeeName = `${updatedEmployee.First_Name || ''} ${updatedEmployee.Last_Name || ''}`.trim() || 'Employee'; showMessage(`${employeeName} updated successfully`, 'success'); } else { // Create new employee const newEmployee = await pb.collection(employeesCollectionName).create(employeeData); const employeeName = `${newEmployee.First_Name || ''} ${newEmployee.Last_Name || ''}`.trim() || 'Employee'; showMessage(`${employeeName} created successfully`, 'success'); } if (typeof closeEmployeeModal === 'function') { closeEmployeeModal(); } loadEmployees(currentPage); } catch (error) { console.error('Error saving employee:', error); const errorMsg = error.message || 'Unknown error'; showMessage('Error saving employee: ' + errorMsg, 'error'); } } /** * Delete employee */ async function deleteEmployee(employeeId) { // Find the employee name for the confirmation message const employee = allEmployees.find(emp => emp.id === employeeId); const employeeName = employee ? `${employee.First_Name || ''} ${employee.Last_Name || ''}`.trim() || 'this employee' : 'this employee'; if (!confirm(`Are you sure you want to delete ${employeeName}? This action cannot be undone.`)) { return; } try { await pb.collection(employeesCollectionName).delete(employeeId); showMessage(`Employee ${employeeName} deleted successfully`, 'success'); // Reload employees with current view state loadEmployees(currentPage); } catch (error) { console.error('Error deleting employee:', error); showMessage('Error deleting employee: ' + (error.message || 'Unknown error'), 'error'); } } /** * Show message to user */ function showMessage(message, type = 'success') { const container = document.getElementById('messageContainer'); if (!container) return; const bgColor = type === 'success' ? 'bg-green-50 border-green-200 text-green-700' : 'bg-red-50 border-red-200 text-red-700'; container.innerHTML = `
${message}
`; // Auto-hide after 5 seconds setTimeout(() => { container.innerHTML = ''; }, 5000); } /** * Switch between table and card view */ function switchView(view) { currentView = view; localStorage.setItem('employeesView', view); updateViewDisplay(); displayEmployees(allEmployees); } /** * Update view display based on current view preference */ function updateViewDisplay() { const tableView = document.getElementById('tableView'); const cardView = document.getElementById('cardView'); const tableViewBtn = document.getElementById('tableViewBtn'); const cardViewBtn = document.getElementById('cardViewBtn'); if (currentView === 'table') { // Show table, hide card if (tableView) tableView.classList.remove('hidden'); if (cardView) cardView.classList.add('hidden'); // Table button: active (indigo with shadow) if (tableViewBtn) { tableViewBtn.classList.remove('text-gray-700', 'hover:bg-white/40'); tableViewBtn.classList.add('bg-indigo-600/90', 'text-white', 'shadow-lg', 'hover:bg-indigo-700/90'); } // Card button: inactive (transparent) if (cardViewBtn) { cardViewBtn.classList.remove('bg-indigo-600/90', 'text-white', 'shadow-lg', 'hover:bg-indigo-700/90'); cardViewBtn.classList.add('text-gray-700', 'hover:bg-white/40'); } } else { // Show card, hide table if (tableView) tableView.classList.add('hidden'); if (cardView) cardView.classList.remove('hidden'); // Table button: inactive (transparent) if (tableViewBtn) { tableViewBtn.classList.remove('bg-indigo-600/90', 'text-white', 'shadow-lg', 'hover:bg-indigo-700/90'); tableViewBtn.classList.add('text-gray-700', 'hover:bg-white/40'); } // Card button: active (indigo with shadow) if (cardViewBtn) { cardViewBtn.classList.remove('text-gray-700', 'hover:bg-white/40'); cardViewBtn.classList.add('bg-indigo-600/90', 'text-white', 'shadow-lg', 'hover:bg-indigo-700/90'); } } } /** * Switch employee modal tab */ function switchEmployeeTab(tabName) { // Hide all tab contents document.querySelectorAll('.employee-tab-content').forEach(tab => { tab.classList.add('hidden'); }); // Remove active state from all tabs document.querySelectorAll('.employee-tab').forEach(btn => { btn.classList.remove('border-indigo-500', 'text-indigo-600'); btn.classList.add('border-transparent', 'text-gray-500'); }); // Show selected tab content const selectedTab = document.getElementById(`employee-tab-${tabName}`); if (selectedTab) { selectedTab.classList.remove('hidden'); } // Activate selected tab button const selectedButton = document.getElementById(`tab-${tabName}`); if (selectedButton) { selectedButton.classList.add('border-indigo-500', 'text-indigo-600'); selectedButton.classList.remove('border-transparent', 'text-gray-500'); } // Load reports if reports tab is selected if (tabName === 'reports' && window.currentEmployeeId && typeof loadEmployeeReports === 'function') { setTimeout(() => { loadEmployeeReports(window.currentEmployeeId); }, 100); } } /** * Update employee modal header info */ function updateEmployeeModalInfo(employee) { const firstName = employee.First_Name || ''; const lastName = employee.Last_Name || ''; const fullName = `${firstName} ${lastName}`.trim() || 'Employee'; document.getElementById('employeeModalTitle').textContent = fullName; } // Make functions globally accessible window.loadEmployees = loadEmployees; window.loadEmployeeForEdit = loadEmployeeForEdit; window.saveEmployee = saveEmployee; window.deleteEmployee = deleteEmployee; window.switchView = switchView; window.updateViewDisplay = updateViewDisplay; window.switchEmployeeTab = switchEmployeeTab;