import { ref } from 'vue' import api from '../api' /** * "Did you mean" for a search that found nothing. * * ONE composable for every list in the product. Thirty routes take a search * parameter across a dozen files, so carrying a suggestion in each of their * responses would be a large change now and a thing every future plugin has to * remember. Instead a page that renders no rows asks for a spelling, and a page * that has not adopted this shows nothing - exactly what it showed before. * * Usage in a list view: * * const { suggestion, checkSuggestion, clearSuggestion } = useSearchSuggestion() * // after a fetch: * items.value.length ? clearSuggestion() : checkSuggestion(searchTerm.value) * * and in the template, beside the empty state: * * */ export function useSearchSuggestion() { const suggestion = ref(null) // Guards a slow answer arriving after a newer search has already been typed, // which would offer a spelling for a query nobody is looking at any more. let latest = 0 function clearSuggestion() { suggestion.value = null latest += 1 } async function checkSuggestion(query) { const term = (query || '').trim() if (term.length < 2) { clearSuggestion() return } const ticket = ++latest try { const response = await api.get('/search/suggest', { params: { q: term } }) if (ticket !== latest) return const offered = response.data?.data?.suggestion || null // Never offer back what was typed: a suggestion identical to the query // reads as a broken feature. suggestion.value = offered && offered !== term.toLowerCase() ? offered : null } catch (err) { // A search that found nothing is already the answer. Failing to improve // on it is not worth an error in front of anyone. if (ticket === latest) suggestion.value = null } } return { suggestion, checkSuggestion, clearSuggestion } }