feat: ip address notes/descriptions

This commit is contained in:
2025-12-27 22:30:54 +00:00
parent b23cda48af
commit 8b001a047b
5 changed files with 246 additions and 15 deletions
+148 -1
View File
@@ -31,8 +31,10 @@ document.addEventListener('DOMContentLoaded', () => {
rows.forEach(row => {
const ipCell = row.querySelector('td:nth-child(1)').textContent.toLowerCase();
const hostnameCell = row.querySelector('td:nth-child(2)').textContent.toLowerCase();
const descCell = row.querySelector('td:nth-child(3)');
const descText = descCell ? descCell.textContent.toLowerCase() : '';
if (ipCell.includes(searchTerm) || hostnameCell.includes(searchTerm)) {
if (ipCell.includes(searchTerm) || hostnameCell.includes(searchTerm) || descText.includes(searchTerm)) {
row.style.backgroundColor = 'rgba(59, 130, 246, 0.5)';
row.scrollIntoView({ behavior: 'smooth', block: 'center' });
@@ -132,4 +134,149 @@ document.addEventListener('DOMContentLoaded', () => {
}, 100);
}
}
// Auto-resize all description textareas (both editable and readonly)
const allDescTextareas = document.querySelectorAll('.desc-col textarea');
allDescTextareas.forEach(textarea => {
textarea.style.overflow = 'hidden';
textarea.style.resize = 'none';
function autoResize() {
textarea.style.height = 'auto';
textarea.style.height = textarea.scrollHeight + 'px';
}
autoResize();
});
// IP Notes inline editing functionality
const ipNotesTextareas = document.querySelectorAll('.ip-notes-textarea');
const originalValues = new Map();
// Helper function to show toast notification
function showToast(message, type = 'success') {
const toast = document.createElement('div');
toast.className = `fixed top-20 right-4 px-4 py-3 rounded-lg shadow-lg z-50 ${
type === 'success'
? 'bg-green-500 text-white'
: 'bg-red-500 text-white'
}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.transition = 'opacity 0.3s';
toast.style.opacity = '0';
setTimeout(() => toast.remove(), 300);
}, 3000);
}
ipNotesTextareas.forEach(textarea => {
// Store original value
originalValues.set(textarea, textarea.value);
// Ensure overflow is hidden and resize is disabled
textarea.style.overflow = 'hidden';
textarea.style.resize = 'none';
// Auto-resize textarea
function autoResize() {
textarea.style.height = 'auto';
textarea.style.height = textarea.scrollHeight + 'px';
}
autoResize();
// Handle input to auto-resize
textarea.addEventListener('input', autoResize);
// Handle blur event to save notes
textarea.addEventListener('blur', async function() {
const ipId = this.getAttribute('data-ip-id');
const deviceDesc = this.getAttribute('data-device-desc') || '';
const fullValue = this.value;
const originalValue = originalValues.get(this);
// Extract IP notes: everything after the device description
let ipNotes = '';
if (deviceDesc) {
// If device description exists, check if textarea starts with it
const deviceDescTrimmed = deviceDesc.trim();
const fullValueTrimmed = fullValue.trim();
if (fullValueTrimmed.startsWith(deviceDescTrimmed)) {
// Remove device description from the beginning
ipNotes = fullValueTrimmed.substring(deviceDescTrimmed.length).trim();
// Also handle case where there's a newline separator
if (ipNotes.startsWith('\n')) {
ipNotes = ipNotes.substring(1).trim();
}
} else {
// Device description was modified or removed - extract everything as IP notes
// This shouldn't normally happen, but handle gracefully
ipNotes = fullValueTrimmed;
}
} else {
// No device description, so entire value is IP notes
ipNotes = fullValue.trim();
}
// Only save if value changed
if (fullValue !== originalValue) {
// Show loading indicator
const originalBg = this.style.backgroundColor;
this.style.backgroundColor = 'rgba(59, 130, 246, 0.2)';
this.disabled = true;
try {
const response = await fetch(`/ip/${ipId}/update_notes`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ notes: ipNotes })
});
const data = await response.json();
if (data.success) {
// Update the displayed value to reflect what was saved
let newDisplayValue = '';
if (deviceDesc) {
newDisplayValue = deviceDesc;
if (ipNotes) {
newDisplayValue += '\n' + ipNotes;
}
} else {
newDisplayValue = ipNotes;
}
this.value = newDisplayValue;
originalValues.set(this, newDisplayValue);
autoResize();
showToast('Notes saved successfully', 'success');
} else {
// Restore original value on error
this.value = originalValue;
autoResize();
showToast(data.error || 'Failed to save notes', 'error');
}
} catch (error) {
// Restore original value on error
this.value = originalValue;
autoResize();
showToast('Error saving notes. Please try again.', 'error');
console.error('Error saving IP notes:', error);
} finally {
this.style.backgroundColor = originalBg;
this.disabled = false;
}
}
});
// Handle Escape key to cancel editing
textarea.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
this.value = originalValues.get(this);
autoResize();
this.blur();
}
});
});
});