update
This commit is contained in:
105
frontend/src/components/SearchTab.vue
Normal file
105
frontend/src/components/SearchTab.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import MovieCard from './MovieCard.vue'
|
||||
|
||||
const emit = defineEmits(['open'])
|
||||
|
||||
const query = ref('')
|
||||
const results = ref([])
|
||||
const loading = ref(false)
|
||||
const error = ref('')
|
||||
const searched = ref(false)
|
||||
|
||||
let timer = null
|
||||
let laufende = 0
|
||||
|
||||
async function run(term) {
|
||||
if (term.trim().length < 2) {
|
||||
results.value = []
|
||||
searched.value = false
|
||||
return
|
||||
}
|
||||
const meine = ++laufende
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
const resp = await fetch(`/api/search?q=${encodeURIComponent(term.trim())}`)
|
||||
if (!resp.ok) throw new Error('Suche fehlgeschlagen')
|
||||
const data = await resp.json()
|
||||
// Eine ältere Anfrage darf ein neueres Ergebnis nicht überschreiben
|
||||
if (meine !== laufende) return
|
||||
results.value = data
|
||||
searched.value = true
|
||||
} catch (err) {
|
||||
if (meine === laufende) error.value = err.message
|
||||
} finally {
|
||||
if (meine === laufende) loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(query, (term) => {
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(() => run(term), 350)
|
||||
})
|
||||
|
||||
async function open(movie) {
|
||||
const resp = await fetch(`/api/search/${movie.id}`)
|
||||
if (resp.ok) emit('open', await resp.json())
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search-tab">
|
||||
<input
|
||||
v-model="query"
|
||||
type="search"
|
||||
class="big-search"
|
||||
placeholder="Film suchen — egal wo er läuft…"
|
||||
autofocus
|
||||
/>
|
||||
|
||||
<p v-if="loading" class="note">Suche läuft…</p>
|
||||
<p v-else-if="error" class="note error">{{ error }}</p>
|
||||
<p v-else-if="searched && !results.length" class="note">
|
||||
Nichts gefunden für „{{ query }}“.
|
||||
</p>
|
||||
<p v-else-if="!searched" class="note">
|
||||
Tippe einen Titel ein. Gesucht wird der ganze TMDB-Bestand, nicht nur
|
||||
der Katalog.
|
||||
</p>
|
||||
|
||||
<div class="grid">
|
||||
<MovieCard
|
||||
v-for="movie in results"
|
||||
:key="movie.id"
|
||||
:movie="movie"
|
||||
@open="open"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.big-search {
|
||||
width: 100%;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 0.9rem 1.1rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
||||
gap: 1.1rem;
|
||||
}
|
||||
|
||||
.note {
|
||||
padding: 2.5rem 0;
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note.error {
|
||||
color: var(--accent);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user