// Custom Fields Management JavaScript // Get initial tab from URL parameter or default to 'device' const urlParams = new URLSearchParams(window.location.search); let currentTab = urlParams.get('tab') || 'device'; if (currentTab !== 'device' && currentTab !== 'subnet') { currentTab = 'device'; } // Switch to the correct tab on page load if (currentTab === 'subnet') { switchTab('subnet'); } else { // Ensure device tab is active on load switchTab('device'); } // Function to get current active tab function getCurrentTab() { return currentTab; } let fieldData = {}; // Tab switching function switchTab(entityType) { currentTab = entityType; // Update tab buttons document.getElementById('tab-device').classList.remove('border-gray-600', 'text-gray-900', 'dark:text-gray-100'); document.getElementById('tab-device').classList.add('border-transparent', 'text-gray-500'); document.getElementById('tab-subnet').classList.remove('border-gray-600', 'text-gray-900', 'dark:text-gray-100'); document.getElementById('tab-subnet').classList.add('border-transparent', 'text-gray-500'); if (entityType === 'device') { document.getElementById('tab-device').classList.remove('border-transparent', 'text-gray-500'); document.getElementById('tab-device').classList.add('border-gray-600', 'text-gray-900', 'dark:text-gray-100'); document.getElementById('device-fields-tab').classList.remove('hidden'); document.getElementById('subnet-fields-tab').classList.add('hidden'); } else { document.getElementById('tab-subnet').classList.remove('border-transparent', 'text-gray-500'); document.getElementById('tab-subnet').classList.add('border-gray-600', 'text-gray-900', 'dark:text-gray-100'); document.getElementById('device-fields-tab').classList.add('hidden'); document.getElementById('subnet-fields-tab').classList.remove('hidden'); } // Update URL without reloading page const newUrl = new URL(window.location); newUrl.searchParams.set('tab', entityType); window.history.pushState({}, '', newUrl); } // Show add field modal function showAddFieldModal(entityType) { // Determine the target entity type - prioritize explicit parameter, then read from DOM let targetEntityType = entityType; if (!targetEntityType) { // Read from active tab button - check which tab has the active styling const deviceTab = document.getElementById('tab-device'); const subnetTab = document.getElementById('tab-subnet'); if (deviceTab && deviceTab.classList.contains('border-gray-600')) { targetEntityType = 'device'; } else if (subnetTab && subnetTab.classList.contains('border-gray-600')) { targetEntityType = 'subnet'; } else { // Fallback to currentTab variable targetEntityType = currentTab || 'device'; } } // Ensure targetEntityType is valid if (targetEntityType !== 'device' && targetEntityType !== 'subnet') { targetEntityType = 'device'; } // Ensure we're on the correct tab if (targetEntityType !== currentTab) { switchTab(targetEntityType); } document.getElementById('modal-title').textContent = 'Add Custom Field'; document.getElementById('form-action').value = 'add_field'; document.getElementById('form-field-id').value = ''; // Always set entity_type explicitly - double check it's set const entityTypeInput = document.getElementById('form-entity-type'); entityTypeInput.value = targetEntityType; // Debug: log to verify console.log('Opening modal for entity type:', targetEntityType, 'currentTab:', currentTab, 'input value:', entityTypeInput.value); // Reset form document.getElementById('field-name').value = ''; document.getElementById('field-key').value = ''; document.getElementById('field-type').value = 'text'; document.getElementById('field-required').checked = false; document.getElementById('field-default-value').value = ''; document.getElementById('field-help-text').value = ''; document.getElementById('field-display-order').value = '0'; // Reset validation fields document.getElementById('field-min-length').value = ''; document.getElementById('field-max-length').value = ''; document.getElementById('field-regex-pattern').value = ''; document.getElementById('field-min-value').value = ''; document.getElementById('field-max-value').value = ''; document.getElementById('field-select-options').value = ''; updateFieldTypeOptions(); document.getElementById('field-modal').classList.remove('hidden'); } // Close field modal function closeFieldModal() { document.getElementById('field-modal').classList.add('hidden'); } // Update field type options visibility function updateFieldTypeOptions() { const fieldType = document.getElementById('field-type').value; // Hide all validation sections document.getElementById('text-validation').classList.add('hidden'); document.getElementById('number-validation').classList.add('hidden'); document.getElementById('select-validation').classList.add('hidden'); // Show relevant validation section if (fieldType === 'text' || fieldType === 'textarea') { document.getElementById('text-validation').classList.remove('hidden'); } else if (fieldType === 'number' || fieldType === 'decimal') { document.getElementById('number-validation').classList.remove('hidden'); } else if (fieldType === 'select') { document.getElementById('select-validation').classList.remove('hidden'); } } // Auto-generate field key from name function generateFieldKey(name) { return name.toLowerCase() .replace(/[^a-z0-9]+/g, '_') .replace(/^_+|_+$/g, ''); } // Edit field function editField(fieldId, entityType) { // Get field data from embedded JSON const fieldsDataElement = document.getElementById('fields-data'); if (!fieldsDataElement) { console.error('Fields data not found'); return; } try { const fieldsData = JSON.parse(fieldsDataElement.textContent); const fields = fieldsData[entityType] || []; const field = fields.find(f => f.id === fieldId); if (field) { populateEditForm(field, entityType); } else { console.error('Field not found:', fieldId, entityType); } } catch (error) { console.error('Error parsing fields data:', error); } } function populateEditForm(field, entityType) { document.getElementById('modal-title').textContent = 'Edit Custom Field'; document.getElementById('form-action').value = 'edit_field'; document.getElementById('form-field-id').value = field.id; document.getElementById('form-entity-type').value = entityType; document.getElementById('field-name').value = field.name || ''; document.getElementById('field-key').value = field.field_key || ''; document.getElementById('field-type').value = field.field_type || 'text'; document.getElementById('field-required').checked = field.required || false; document.getElementById('field-default-value').value = field.default_value || ''; document.getElementById('field-help-text').value = field.help_text || ''; document.getElementById('field-display-order').value = field.display_order || 0; // Parse validation rules let validationRules = {}; if (field.validation_rules) { if (typeof field.validation_rules === 'string') { try { validationRules = JSON.parse(field.validation_rules); } catch (e) { validationRules = {}; } } else { validationRules = field.validation_rules; } } // Populate validation fields document.getElementById('field-min-length').value = validationRules.min_length || ''; document.getElementById('field-max-length').value = validationRules.max_length || ''; document.getElementById('field-regex-pattern').value = validationRules.regex_pattern || ''; document.getElementById('field-min-value').value = validationRules.min_value || ''; document.getElementById('field-max-value').value = validationRules.max_value || ''; if (validationRules.select_options) { document.getElementById('field-select-options').value = validationRules.select_options.join(', '); } else { document.getElementById('field-select-options').value = ''; } updateFieldTypeOptions(); document.getElementById('field-modal').classList.remove('hidden'); } // Move field up/down function moveField(entityType, fieldId, direction) { // Get all fields for this entity type const tbody = document.getElementById(`${entityType}-fields-tbody`); const rows = Array.from(tbody.querySelectorAll('tr')); const currentIndex = rows.findIndex(row => row.dataset.fieldId == fieldId); if (currentIndex === -1) return; let targetIndex; if (direction === 'up' && currentIndex > 0) { targetIndex = currentIndex - 1; } else if (direction === 'down' && currentIndex < rows.length - 1) { targetIndex = currentIndex + 1; } else { return; } // Swap rows const currentRow = rows[currentIndex]; const targetRow = rows[targetIndex]; tbody.insertBefore(currentRow, direction === 'up' ? targetRow : targetRow.nextSibling); // Update display orders and submit const fieldOrders = {}; Array.from(tbody.querySelectorAll('tr')).forEach((row, index) => { fieldOrders[row.dataset.fieldId] = index; }); // Submit reorder const form = document.createElement('form'); form.method = 'POST'; form.action = '/custom_fields'; const actionInput = document.createElement('input'); actionInput.type = 'hidden'; actionInput.name = 'action'; actionInput.value = 'reorder'; form.appendChild(actionInput); const entityTypeInput = document.createElement('input'); entityTypeInput.type = 'hidden'; entityTypeInput.name = 'entity_type'; entityTypeInput.value = entityType; form.appendChild(entityTypeInput); const ordersInput = document.createElement('input'); ordersInput.type = 'hidden'; ordersInput.name = 'field_orders'; ordersInput.value = JSON.stringify(fieldOrders); form.appendChild(ordersInput); document.body.appendChild(form); form.submit(); } // Event listeners document.addEventListener('DOMContentLoaded', function() { // Auto-generate field key from name const nameInput = document.getElementById('field-name'); const keyInput = document.getElementById('field-key'); if (nameInput && keyInput) { nameInput.addEventListener('input', function() { // Only auto-generate if key is empty or matches previous generated value if (!keyInput.value || keyInput.dataset.autoGenerated === 'true') { keyInput.value = generateFieldKey(this.value); keyInput.dataset.autoGenerated = 'true'; } }); keyInput.addEventListener('input', function() { // Mark as manually edited this.dataset.autoGenerated = 'false'; }); } // Update field type options when type changes const fieldTypeSelect = document.getElementById('field-type'); if (fieldTypeSelect) { fieldTypeSelect.addEventListener('change', updateFieldTypeOptions); } // Ensure entity_type is set correctly before form submission const fieldForm = document.getElementById('field-form'); if (fieldForm) { fieldForm.addEventListener('submit', function(e) { const entityTypeInput = document.getElementById('form-entity-type'); // Always ensure entity_type is set to currentTab // This handles cases where the modal was opened without explicitly setting it if (!entityTypeInput.value || entityTypeInput.value.trim() === '') { entityTypeInput.value = currentTab; console.log('Entity type was empty, setting to:', currentTab); } // Double-check it's a valid value if (entityTypeInput.value !== 'device' && entityTypeInput.value !== 'subnet') { entityTypeInput.value = currentTab; console.log('Entity type was invalid, setting to currentTab:', currentTab); } console.log('Submitting form with entity_type:', entityTypeInput.value, 'currentTab:', currentTab); }); } }); // Close modal when clicking outside window.onclick = function(event) { const modal = document.getElementById('field-modal'); if (event.target === modal) { closeFieldModal(); } }