const API_BASE = '/api/links'; // DOM elements const linkForm = document.getElementById('linkForm'); const linkInput = document.getElementById('linkInput'); const addButton = document.getElementById('addButton'); const searchInput = document.getElementById('searchInput'); const linksContainer = document.getElementById('linksContainer'); const messageDiv = document.getElementById('message'); // State let allLinks = []; let searchTimeout = null; // Initialize app document.addEventListener('DOMContentLoaded', () => { loadLinks(); setupEventListeners(); }); // Event listeners function setupEventListeners() { linkForm.addEventListener('submit', handleAddLink); searchInput.addEventListener('input', handleSearch); } // Load all links async function loadLinks() { try { const response = await fetch(API_BASE); if (!response.ok) throw new Error('Failed to load links'); allLinks = await response.json(); displayLinks(allLinks); } catch (error) { showMessage('Failed to load links', 'error'); console.error('Error loading links:', error); } } // Handle add link form submission async function handleAddLink(e) { e.preventDefault(); const url = linkInput.value.trim(); if (!url) return; // Disable form addButton.disabled = true; const btnText = addButton.querySelector('.btn-text'); const btnLoader = addButton.querySelector('.btn-loader'); btnText.style.display = 'none'; btnLoader.style.display = 'inline'; try { const response = await fetch(API_BASE, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Failed to add link'); } // Add to beginning of array allLinks.unshift(data); displayLinks(allLinks); // Clear input linkInput.value = ''; showMessage('Link added successfully!', 'success'); } catch (error) { showMessage(error.message, 'error'); console.error('Error adding link:', error); } finally { // Re-enable form addButton.disabled = false; btnText.style.display = 'inline'; btnLoader.style.display = 'none'; } } // Handle search input function handleSearch(e) { const query = e.target.value.trim(); // Debounce search clearTimeout(searchTimeout); searchTimeout = setTimeout(async () => { if (!query) { displayLinks(allLinks); return; } try { const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(query)}`); if (!response.ok) throw new Error('Search failed'); const filteredLinks = await response.json(); displayLinks(filteredLinks); } catch (error) { showMessage('Search failed', 'error'); console.error('Error searching:', error); } }, 300); } // Display links function displayLinks(links) { if (links.length === 0) { linksContainer.innerHTML = `

No links found. ${allLinks.length === 0 ? 'Add your first link to get started!' : 'Try a different search term.'}

`; return; } linksContainer.innerHTML = links.map(link => createLinkCard(link)).join(''); // Add delete event listeners document.querySelectorAll('.delete-btn').forEach(btn => { btn.addEventListener('click', (e) => { const linkId = e.target.closest('.link-card').dataset.id; handleDeleteLink(linkId); }); }); } // Create link card HTML function createLinkCard(link) { const date = new Date(link.createdAt); const formattedDate = date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); const imageHtml = link.image ? `${escapeHtml(link.title)}` : 'No image available'; return ` `; } // Handle delete link async function handleDeleteLink(id) { if (!confirm('Are you sure you want to delete this link?')) { return; } try { const response = await fetch(`${API_BASE}/${id}`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete link'); // Remove from local array allLinks = allLinks.filter(link => link.id !== id); displayLinks(allLinks); showMessage('Link deleted successfully', 'success'); } catch (error) { showMessage('Failed to delete link', 'error'); console.error('Error deleting link:', error); } } // Show message function showMessage(text, type = 'success') { messageDiv.textContent = text; messageDiv.className = `message ${type}`; messageDiv.style.display = 'block'; setTimeout(() => { messageDiv.style.display = 'none'; }, 3000); } // Escape HTML to prevent XSS function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }