import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = [ "query", "results", "igdbId", "title", "summary", "platformSelect", "customGameToggle", "igdbSection", "manualSection" ] static values = { url: String } connect() { console.log("IGDB Search controller connected!") console.log("Search URL:", this.urlValue) console.log("Query target exists:", this.hasQueryTarget) console.log("Results target exists:", this.hasResultsTarget) console.log("Title target exists:", this.hasTitleTarget) this.timeout = null this.selectedGame = null this.searchResults = [] } search() { console.log("Search triggered") clearTimeout(this.timeout) const query = this.queryTarget.value.trim() console.log("Query:", query) if (query.length < 2) { console.log("Query too short, hiding results") this.hideResults() return } console.log("Starting search timeout...") this.timeout = setTimeout(() => { this.performSearch(query) }, 300) } async performSearch(query) { const platformId = this.hasPlatformSelectTarget ? this.platformSelectTarget.value : "" const url = `${this.urlValue}?q=${encodeURIComponent(query)}&platform_id=${platformId}` console.log("Fetching:", url) try { const response = await fetch(url) console.log("Response status:", response.status) const results = await response.json() console.log("Results:", results) this.displayResults(results) } catch (error) { console.error("IGDB search error:", error) } } displayResults(results) { if (results.length === 0) { this.resultsTarget.innerHTML = `
No games found. Add custom game
` this.resultsTarget.classList.remove("hidden") return } // Store results in memory and use indices instead of embedding JSON this.searchResults = results const html = results.map((game, index) => { // HTML escape function const escapeHtml = (text) => { const div = document.createElement('div') div.textContent = text return div.innerHTML } return `
${game.cover_url ? ` ${escapeHtml(game.name)} ` : `
No
Cover
`}
${escapeHtml(game.name)}
${escapeHtml(game.platform)}${game.year ? ` • ${game.year}` : ''}
${game.genres && game.genres.length > 0 ? `
${game.genres.slice(0, 3).map(genre => ` ${escapeHtml(genre)} `).join('')}
` : ''}
${Math.round(game.confidence)}% match
` }).join('') this.resultsTarget.innerHTML = html this.resultsTarget.classList.remove("hidden") } selectGame(event) { const index = parseInt(event.currentTarget.dataset.index) const gameData = this.searchResults[index] this.selectedGame = gameData console.log("Selected game:", gameData) // Fill in the form fields if (this.hasTitleTarget) { this.titleTarget.value = gameData.name console.log("Set title to:", gameData.name) } else { console.warn("Title target not found") } if (this.hasIgdbIdTarget) { this.igdbIdTarget.value = gameData.igdb_id console.log("Set IGDB ID to:", gameData.igdb_id) } else { console.warn("IGDB ID target not found") } if (this.hasSummaryTarget && gameData.summary) { this.summaryTarget.value = gameData.summary console.log("Set summary") } // Set genres if (gameData.genre_ids && gameData.genre_ids.length > 0) { this.setGenres(gameData.genre_ids) } // Update the query field to show selected game this.queryTarget.value = gameData.name // Hide results this.hideResults() // Show IGDB badge/info this.showIgdbInfo(gameData) } setGenres(genreIds) { console.log("Setting genres:", genreIds) // Find all genre checkboxes const genreCheckboxes = document.querySelectorAll('input[name="game[genre_ids][]"]') // Uncheck all first genreCheckboxes.forEach(checkbox => { checkbox.checked = false }) // Check the matched genres genreIds.forEach(genreId => { const checkbox = document.querySelector(`input[name="game[genre_ids][]"][value="${genreId}"]`) if (checkbox) { checkbox.checked = true console.log("Checked genre:", genreId) } }) } showIgdbInfo(gameData) { // Add a visual indicator that this is from IGDB const badge = document.createElement('div') badge.className = 'mt-2 space-y-2' let genreText = '' if (gameData.genres && gameData.genres.length > 0) { genreText = `
Genres auto-set: ${gameData.genres.join(', ')}
` } badge.innerHTML = `
IGDB Match (${Math.round(gameData.confidence)}% confidence)
${genreText} ` // Insert after query field const existingBadge = this.queryTarget.parentElement.querySelector('.igdb-match-badge') if (existingBadge) { existingBadge.remove() } badge.classList.add('igdb-match-badge') this.queryTarget.parentElement.appendChild(badge) } showManualEntry(event) { event.preventDefault() // Clear IGDB fields if (this.hasIgdbIdTarget) { this.igdbIdTarget.value = "" } // Focus on title field if (this.hasTitleTarget) { this.titleTarget.focus() } this.hideResults() // Remove IGDB badge const badge = this.queryTarget.parentElement.querySelector('.igdb-match-badge') if (badge) { badge.remove() } } hideResults() { if (this.hasResultsTarget) { this.resultsTarget.classList.add("hidden") } } clearSearch() { this.queryTarget.value = "" this.hideResults() if (this.hasTitleTarget) { this.titleTarget.value = "" } if (this.hasIgdbIdTarget) { this.igdbIdTarget.value = "" } if (this.hasSummaryTarget) { this.summaryTarget.value = "" } // Uncheck all genres const genreCheckboxes = document.querySelectorAll('input[name="game[genre_ids][]"]') genreCheckboxes.forEach(checkbox => { checkbox.checked = false }) const badge = this.queryTarget.parentElement.querySelector('.igdb-match-badge') if (badge) { badge.remove() } } // Hide results when clicking outside clickOutside(event) { if (!this.element.contains(event.target)) { this.hideResults() } } }