// Assessment-specific JavaScript functionality // Enhanced drag and drop for assessment class AssessmentDragDrop { constructor() { this.draggedElement = null; this.initializeDragDrop(); } initializeDragDrop() { // Set up drag and drop for capability items document.addEventListener('dragstart', (e) => { if (e.target.classList.contains('capability-item')) { this.handleDragStart(e); } }); document.addEventListener('dragover', (e) => { if (e.target.closest('.drop-zone')) { this.handleDragOver(e); } }); document.addEventListener('dragleave', (e) => { if (e.target.closest('.drop-zone')) { this.handleDragLeave(e); } }); document.addEventListener('drop', (e) => { if (e.target.closest('.drop-zone')) { this.handleDrop(e); } }); document.addEventListener('dragend', (e) => { if (e.target.classList.contains('capability-item')) { this.handleDragEnd(e); } }); } handleDragStart(e) { this.draggedElement = e.target; e.target.classList.add('dragging'); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/html', e.target.outerHTML); e.dataTransfer.setData('text/plain', e.target.dataset.capabilityId); } handleDragOver(e) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; const dropZone = e.target.closest('.drop-zone'); if (dropZone) { dropZone.classList.add('drag-over'); } } handleDragLeave(e) { const dropZone = e.target.closest('.drop-zone'); if (dropZone && !dropZone.contains(e.relatedTarget)) { dropZone.classList.remove('drag-over'); } } handleDrop(e) { e.preventDefault(); const dropZone = e.target.closest('.drop-zone'); if (dropZone) { dropZone.classList.remove('drag-over'); if (this.draggedElement) { this.moveCapability(this.draggedElement, dropZone); } } } handleDragEnd(e) { e.target.classList.remove('dragging'); this.draggedElement = null; } moveCapability(capabilityElement, targetDropZone) { // Remove from original position capabilityElement.remove(); // Create new element in target drop zone const newElement = this.createCapabilityElement(capabilityElement); // Clear target drop zone and add new element targetDropZone.innerHTML = ''; targetDropZone.appendChild(newElement); targetDropZone.classList.add('has-items'); // Update visual state this.updateDropZoneState(targetDropZone); } createCapabilityElement(originalElement) { const newElement = document.createElement('div'); newElement.className = 'capability-item'; newElement.draggable = true; newElement.dataset.capabilityId = originalElement.dataset.capabilityId; newElement.dataset.capabilityName = originalElement.dataset.capabilityName; newElement.textContent = originalElement.dataset.capabilityName; // Add click to remove functionality newElement.addEventListener('dblclick', () => { this.removeCapabilityFromLevel(newElement); }); return newElement; } removeCapabilityFromLevel(capabilityElement) { const dropZone = capabilityElement.closest('.drop-zone'); if (dropZone) { // Return to available capabilities this.returnToAvailable(capabilityElement); // Update drop zone state this.updateDropZoneState(dropZone); } } returnToAvailable(capabilityElement) { const availableContainer = document.getElementById('available-capabilities'); if (availableContainer) { const newElement = this.createCapabilityElement(capabilityElement); availableContainer.appendChild(newElement); } } updateDropZoneState(dropZone) { const hasItems = dropZone.querySelector('.capability-item'); if (hasItems) { dropZone.classList.add('has-items'); dropZone.classList.remove('empty'); } else { dropZone.classList.remove('has-items'); dropZone.classList.add('empty'); dropZone.innerHTML = '
拖放能力到此處
'; } } } // Capability management class CapabilityManager { constructor() { this.capabilities = []; this.loadCapabilities(); } async loadCapabilities() { try { const response = await fetch('/api/capabilities'); const data = await response.json(); this.capabilities = data.capabilities; this.displayCapabilities(); } catch (error) { console.error('Failed to load capabilities:', error); this.showError('載入能力清單失敗'); } } displayCapabilities() { const container = document.getElementById('available-capabilities'); if (!container) return; container.innerHTML = ''; this.capabilities.forEach(capability => { const capabilityElement = this.createCapabilityElement(capability); container.appendChild(capabilityElement); }); } createCapabilityElement(capability) { const element = document.createElement('div'); element.className = 'capability-item'; element.draggable = true; element.dataset.capabilityId = capability.id; element.dataset.capabilityName = capability.name; element.textContent = capability.name; // Add tooltip with description element.title = this.getCapabilityDescription(capability); return element; } getCapabilityDescription(capability) { const descriptions = [ capability.l1_description, capability.l2_description, capability.l3_description, capability.l4_description, capability.l5_description ].filter(desc => desc && desc.trim()); return descriptions.join('\n\n'); } showError(message) { // You can implement a more sophisticated error display here console.error(message); } } // Assessment form validation and submission class AssessmentForm { constructor() { this.form = document.getElementById('assessment-form'); this.initializeForm(); } initializeForm() { if (this.form) { this.form.addEventListener('submit', (e) => { e.preventDefault(); this.handleSubmit(); }); } } async handleSubmit() { if (!this.validateForm()) { return; } const assessmentData = this.collectAssessmentData(); try { this.showLoading(true); const response = await fetch('/api/assessments', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(assessmentData) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); this.showSuccess(result.message); this.clearForm(); } catch (error) { console.error('Assessment submission failed:', error); this.showError('評估提交失敗: ' + error.message); } finally { this.showLoading(false); } } validateForm() { const department = document.getElementById('department').value.trim(); const position = document.getElementById('position').value.trim(); if (!department) { this.showError('請填寫部門'); return false; } if (!position) { this.showError('請填寫職位'); return false; } // Check if at least one capability is assigned const hasAssignedCapabilities = this.hasAssignedCapabilities(); if (!hasAssignedCapabilities) { this.showError('請至少分配一個能力到某個等級'); return false; } return true; } hasAssignedCapabilities() { const levels = ['L1', 'L2', 'L3', 'L4', 'L5']; for (let level of levels) { const dropZone = document.getElementById(`level-${level.toLowerCase()}`); if (dropZone && dropZone.querySelector('.capability-item')) { return true; } } return false; } collectAssessmentData() { const formData = new FormData(this.form); const assessmentData = { department: formData.get('department'), position: formData.get('position'), employee_name: formData.get('employee_name') || null, assessment_data: {} }; // Collect capability assignments const levels = ['L1', 'L2', 'L3', 'L4', 'L5']; levels.forEach(level => { const dropZone = document.getElementById(`level-${level.toLowerCase()}`); const capabilityItems = dropZone.querySelectorAll('.capability-item'); assessmentData.assessment_data[level] = Array.from(capabilityItems).map(item => item.dataset.capabilityName); }); return assessmentData; } clearForm() { this.form.reset(); // Clear all drop zones const levels = ['L1', 'L2', 'L3', 'L4', 'L5']; levels.forEach(level => { const dropZone = document.getElementById(`level-${level.toLowerCase()}`); if (dropZone) { dropZone.innerHTML = '
拖放能力到此處
'; dropZone.classList.remove('has-items'); } }); // Reload capabilities if (window.capabilityManager) { window.capabilityManager.loadCapabilities(); } } showLoading(show) { const submitButton = this.form.querySelector('button[type="submit"]'); if (submitButton) { if (show) { submitButton.disabled = true; submitButton.innerHTML = ' 儲存中...'; } else { submitButton.disabled = false; submitButton.innerHTML = '儲存評估'; } } } showSuccess(message) { // You can implement a more sophisticated success display here console.log('Success:', message); if (window.showSuccess) { window.showSuccess(message); } } showError(message) { // You can implement a more sophisticated error display here console.error('Error:', message); if (window.showError) { window.showError(message); } } } // Initialize assessment functionality when DOM is loaded document.addEventListener('DOMContentLoaded', function() { // Initialize drag and drop window.assessmentDragDrop = new AssessmentDragDrop(); // Initialize capability manager window.capabilityManager = new CapabilityManager(); // Initialize assessment form window.assessmentForm = new AssessmentForm(); }); // Global function for clearing assessment (called from HTML) function clearAssessment() { if (window.assessmentForm) { window.assessmentForm.clearForm(); } }