This commit is contained in:
team3
2026-08-31 11:54:20 +02:00
parent f29a4106bb
commit e7216138c7
21 changed files with 1517 additions and 87 deletions

View File

@@ -1,9 +1,11 @@
<script setup>
import { computed, onMounted, ref, watch } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import MovieCard from './components/MovieCard.vue'
import FilterSidebar from './components/FilterSidebar.vue'
import MovieDetail from './components/MovieDetail.vue'
import SearchTab from './components/SearchTab.vue'
import StreamTab from './components/StreamTab.vue'
import WatchedTab from './components/WatchedTab.vue'
import WheelTab from './components/WheelTab.vue'
import SeriesCard from './components/SeriesCard.vue'
@@ -14,7 +16,13 @@ const error = ref('')
const search = ref('')
const sort = ref('rating')
const selected = ref(null)
const activeTab = ref('netflix')
// Nach dem Neuladen soll derselbe Reiter offen sein
const TAB_SCHLUESSEL = 'filmabend_tab'
const TAB_IDS = ['netflix', 'canon', 'search', 'wheel', 'watched', 'stream']
const gemerkterTab = localStorage.getItem(TAB_SCHLUESSEL)
const activeTab = ref(TAB_IDS.includes(gemerkterTab) ? gemerkterTab : 'netflix')
watch(activeTab, (id) => localStorage.setItem(TAB_SCHLUESSEL, id))
const include = ref([])
const exclude = ref([])
@@ -187,13 +195,48 @@ const netflixEntries = computed(() => {
const tabs = computed(() => [
{ id: 'netflix', label: 'Netflix', count: netflixMovies.value.length },
{ id: 'canon', label: 'Kanon', count: canonGroups.value.length },
{ id: 'canon', label: 'Horror-Kanon', count: canonGroups.value.length },
{ id: 'search', label: 'Suche', count: null },
{ id: 'wheel', label: 'Rad', count: null },
{ id: 'watched', label: 'Gesehen', count: gesehenZahl.value },
{ id: 'stream', label: 'Stream', count: null },
])
const rad = ref(null)
// Die movie_ids der geschauten Filme — die Detailansicht zeigt damit an,
// ob der Film schon lief
const gesehen = ref([])
async function ladeGesehen() {
try {
const resp = await fetch('/api/watched')
if (resp.ok) gesehen.value = await resp.json()
} catch {
/* dann eben ohne */
}
}
// Ausgeblendete Filme zählen für Gäste nicht mit
const gesehenZahl = computed(
() => gesehen.value.filter((e) => host.value || !e.hidden).length
)
// Wer den Abend leitet, darf drehen und die Runde leeren. Kein Login —
// die Tastenkombination reicht, die Seite steht ohnehin nur Freunden offen.
const HOST_SCHLUESSEL = 'filmabend_host'
const host = ref(localStorage.getItem(HOST_SCHLUESSEL) === '1')
function tastatur(e) {
// e.code statt e.key: mit drei Zusatztasten liefert das Layout sonst
// je nach System ein anderes Zeichen
if (e.ctrlKey && e.shiftKey && e.altKey && e.code === 'KeyS') {
e.preventDefault()
host.value = !host.value
localStorage.setItem(HOST_SCHLUESSEL, host.value ? '1' : '0')
}
}
// Wer schon vorgeschlagen hat, soll das in der Detailansicht sehen
const meinVorschlag = ref(null)
@@ -326,11 +369,15 @@ async function load() {
onMounted(() => {
load()
ladeVorschlag()
ladeGesehen()
window.addEventListener('keydown', tastatur)
})
onUnmounted(() => window.removeEventListener('keydown', tastatur))
</script>
<template>
<div class="page" :class="{ breit: activeTab === 'search' || activeTab === 'wheel' }">
<div class="page" :class="{ breit: ['search', 'wheel', 'stream', 'watched'].includes(activeTab) }">
<nav class="tabs">
<button
v-for="tab in tabs"
@@ -342,11 +389,27 @@ onMounted(() => {
{{ tab.label }}
<span v-if="tab.count !== null" class="count">{{ tab.count }}</span>
</button>
<span v-if="host" class="host">Host</span>
</nav>
<SearchTab v-if="activeTab === 'search'" @open="selected = $event" />
<WheelTab v-else-if="activeTab === 'wheel'" ref="rad" @open="selected = $event" />
<WheelTab
v-else-if="activeTab === 'wheel'"
ref="rad"
:host="host"
@open="selected = $event"
@reset="meinVorschlag = null"
/>
<WatchedTab
v-else-if="activeTab === 'watched'"
:host="host"
@open="selected = $event"
@changed="ladeGesehen"
/>
<StreamTab v-else-if="activeTab === 'stream'" />
<div v-else class="layout">
<div class="results">
@@ -402,7 +465,10 @@ onMounted(() => {
@close="selected = null"
@open="selected = $event"
:mein-vorschlag="meinVorschlag"
:host="host"
:gesehen="gesehen.some((e) => e.movie_id === selected.id)"
@suggested="nachVorschlag"
@watched="ladeGesehen"
/>
</div>
@@ -446,6 +512,19 @@ onMounted(() => {
border-bottom-color: var(--accent);
}
.host {
align-self: center;
margin-left: auto;
padding: 0.15rem 0.5rem;
color: var(--accent);
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
border: 1px solid var(--accent);
border-radius: 4px;
}
.tabs .count {
padding: 0.1rem 0.45rem;
font-size: 0.75rem;