// --- Configuration --- // IMPORTANT: For production, use a SEARCH-ONLY API Key. // NEVER expose your Admin API Key in client-side code. // Best practice is to proxy requests through your backend // or use Typesense Scoped Search Keys. const TYPESENSE_CONFIG = { nodes: [{ host: '192.168.2.203', // Or your Typesense host port: '8108', protocol: 'http' }], apiKey: 'imdiowenxodjioqwenlj', // Generate a search-only API key in Typesense connectionTimeoutSeconds: 2 }; const typesenseClient = new Typesense.Client(TYPESENSE_CONFIG); // --- DOM Elements --- const searchInput = document.getElementById('user-search-input'); const suggestionsList = document.getElementById('suggestions-list'); const selectedUserIdEl = document.getElementById('selected-user-id'); const selectedUserNameEl = document.getElementById('selected-user-name'); // --- Debounce Function (to limit API calls) --- let debounceTimer; function debounce(func, delay) { return function(...args) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => func.apply(this, args), delay); }; } // --- Fetch and Display Suggestions --- async function fetchSuggestions(query) { if (!query.trim()) { suggestionsList.innerHTML = ''; suggestionsList.style.display = 'none'; return; } const searchParameters = { 'q': query, 'query_by': 'user_name', // Field to search in 'per_page': 5, // Number of suggestions to show // 'sort_by': '_text_match:desc', // Default, or 'user_name_lowercase_sort:asc' if you use that field 'infix': 'always', // Ensure infix search is active if not default for the field // 'filter_by': 'is_active:=true' // Example: if you have an is_active boolean field }; try { const searchResults = await typesenseClient.collections('users').documents().search(searchParameters); displaySuggestions(searchResults.hits || []); } catch (error) { console.error('Typesense search error:', error); suggestionsList.innerHTML = '
  • Error fetching suggestions
  • '; suggestionsList.style.display = 'block'; } } function displaySuggestions(hits) { suggestionsList.innerHTML = ''; // Clear previous suggestions if (hits.length === 0) { suggestionsList.style.display = 'none'; return; } hits.forEach(hit => { const user = hit.document; // The actual document data const li = document.createElement('li'); // Create a highlighted version of the name if available let displayName = user.user_name; if (hit.highlights && hit.highlights.length > 0) { const nameHighlight = hit.highlights.find(h => h.field === 'user_name'); if (nameHighlight && nameHighlight.snippet) { displayName = nameHighlight.snippet; // Use Typesense's highlighted version } } li.innerHTML = displayName; // Display name (can be highlighted) // Store full user data (or just ID) for selection li.dataset.userId = user.user_id; li.dataset.userName = user.user_name; // Store the original, non-highlighted name li.addEventListener('click', () => { searchInput.value = user.user_name; // Fill input with selected name selectedUserIdEl.textContent = user.user_id; selectedUserNameEl.textContent = user.user_name; suggestionsList.innerHTML = ''; // Clear suggestions suggestionsList.style.display = 'none'; // You now have user.user_id and user.user_name console.log('Selected:', { id: user.user_id, name: user.user_name }); // Here, you might want to submit a form, make another API call, etc. }); suggestionsList.appendChild(li); }); suggestionsList.style.display = 'block'; } // --- Event Listeners --- searchInput.addEventListener('input', debounce((event) => { fetchSuggestions(event.target.value); }, 300)); // 300ms debounce // Optional: Hide suggestions when clicking outside document.addEventListener('click', (event) => { if (!searchInput.contains(event.target) && !suggestionsList.contains(event.target)) { suggestionsList.style.display = 'none'; } }); console.log("Autocomplete App Initialized. Make sure your Typesense server is running and accessible."); console.log("And ensure you've replaced 'YOUR_SEARCH_ONLY_API_KEY' with a valid key.");