Filme: DB-Migration, 68 Einträge, Sort + Typ-Filter
- movies-Tabelle in SQLite (statt hardcoded Liste) - seed_movies(): 68 Filme/Serien/Dokus beim ersten Start - Felder: titel, originaltitel, jahr, genre, typ, hund_rasse, stirbt_der_hund, beschreibung, bild_emoji, imdb_rating, streaming - GET /api/movies/filme?sort=&typ= — serverseitig sortiert Sort: default | titel | jahr_desc | jahr_asc | imdb | bewertung Typ: alle | film | serie | doku - Admin-CRUD: POST/PATCH/DELETE /api/movies/filme - Frontend: Sort-Dropdown, Typ-Filter-Buttons (Filme/Serien/Dokus), Zähler, IMDb-Rating + Streaming auf der Karte - Promis ebenfalls erweitert (10 statt 6 Einträge)
This commit is contained in:
parent
de1677154f
commit
59856e61a1
4 changed files with 350 additions and 113 deletions
|
|
@ -13,6 +13,8 @@ window.Page_movies = (() => {
|
|||
let _filme = [];
|
||||
let _activeTab = 'filme';
|
||||
let _filter = 'alle';
|
||||
let _typ = 'alle'; // alle | film | serie | doku
|
||||
let _sort = 'default'; // default | titel | jahr_desc | jahr_asc | imdb | bewertung
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// INIT
|
||||
|
|
@ -70,20 +72,44 @@ window.Page_movies = (() => {
|
|||
// ----------------------------------------------------------
|
||||
// TAB 1: FILME
|
||||
// ----------------------------------------------------------
|
||||
async function _loadFilme() {
|
||||
_filme = await API.get(`/movies/filme?sort=${_sort}&typ=${_typ}`);
|
||||
}
|
||||
|
||||
async function _renderFilme(content) {
|
||||
try {
|
||||
_filme = await API.get('/movies/filme');
|
||||
await _loadFilme();
|
||||
} catch {
|
||||
content.innerHTML = UI.emptyState({ icon: 'film-slate', title: 'Filme konnten nicht geladen werden', text: 'Bitte versuche es erneut.' });
|
||||
return;
|
||||
}
|
||||
|
||||
content.innerHTML = `
|
||||
<div class="movies-filter-row">
|
||||
<button class="movies-filter-btn${_filter === 'alle' ? ' movies-filter-btn--active' : ''}" data-filter="alle">Alle</button>
|
||||
<button class="movies-filter-btn${_filter === 'stirbt' ? ' movies-filter-btn--active' : ''}" data-filter="stirbt"><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#warning"></use></svg> Hund stirbt</button>
|
||||
<button class="movies-filter-btn${_filter === 'ueberlebt' ? ' movies-filter-btn--active' : ''}" data-filter="ueberlebt"><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#paw-print"></use></svg> Hund überlebt</button>
|
||||
<button class="movies-filter-btn${_filter === 'top' ? ' movies-filter-btn--active' : ''}" data-filter="top"><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#star"></use></svg><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#star"></use></svg><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#star"></use></svg><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#star"></use></svg>+</button>
|
||||
<div class="movies-controls">
|
||||
<div class="movies-filter-row">
|
||||
<button class="movies-filter-btn${_filter === 'alle' ? ' movies-filter-btn--active' : ''}" data-filter="alle">Alle</button>
|
||||
<button class="movies-filter-btn${_filter === 'stirbt' ? ' movies-filter-btn--active' : ''}" data-filter="stirbt"><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#warning"></use></svg> Hund stirbt</button>
|
||||
<button class="movies-filter-btn${_filter === 'ueberlebt' ? ' movies-filter-btn--active' : ''}" data-filter="ueberlebt"><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#paw-print"></use></svg> Hund überlebt</button>
|
||||
<button class="movies-filter-btn${_filter === 'top' ? ' movies-filter-btn--active' : ''}" data-filter="top"><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#star"></use></svg> Top</button>
|
||||
</div>
|
||||
<div class="movies-filter-row" style="margin-top:var(--space-2)">
|
||||
<button class="movies-type-btn${_typ === 'alle' ? ' movies-filter-btn--active' : ''}" data-typ="alle">Alle Typen</button>
|
||||
<button class="movies-type-btn${_typ === 'film' ? ' movies-filter-btn--active' : ''}" data-typ="film">🎬 Filme</button>
|
||||
<button class="movies-type-btn${_typ === 'serie' ? ' movies-filter-btn--active' : ''}" data-typ="serie">📺 Serien</button>
|
||||
<button class="movies-type-btn${_typ === 'doku' ? ' movies-filter-btn--active' : ''}" data-typ="doku">🎥 Dokus</button>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:var(--space-2);margin-top:var(--space-2)">
|
||||
<svg class="ph-icon" aria-hidden="true" style="width:16px;height:16px;color:var(--c-text-muted)"><use href="/icons/phosphor.svg#sort-ascending"></use></svg>
|
||||
<select id="movies-sort" class="form-control" style="flex:1;font-size:var(--text-sm);padding:var(--space-2) var(--space-3)">
|
||||
<option value="default" ${_sort==='default' ?'selected':''}>Empfohlen</option>
|
||||
<option value="bewertung" ${_sort==='bewertung' ?'selected':''}>Community-Bewertung</option>
|
||||
<option value="imdb" ${_sort==='imdb' ?'selected':''}>IMDb-Bewertung</option>
|
||||
<option value="jahr_desc" ${_sort==='jahr_desc' ?'selected':''}>Neueste zuerst</option>
|
||||
<option value="jahr_asc" ${_sort==='jahr_asc' ?'selected':''}>Älteste zuerst</option>
|
||||
<option value="titel" ${_sort==='titel' ?'selected':''}>Titel A–Z</option>
|
||||
</select>
|
||||
<span id="movies-count" style="font-size:var(--text-xs);color:var(--c-text-muted);white-space:nowrap"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="movie-grid" id="movie-grid"></div>
|
||||
`;
|
||||
|
|
@ -97,6 +123,26 @@ window.Page_movies = (() => {
|
|||
});
|
||||
});
|
||||
|
||||
content.querySelectorAll('.movies-type-btn').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
_typ = btn.dataset.typ;
|
||||
content.querySelectorAll('.movies-type-btn').forEach(b => b.classList.remove('movies-filter-btn--active'));
|
||||
btn.classList.add('movies-filter-btn--active');
|
||||
const grid = content.querySelector('#movie-grid');
|
||||
grid.innerHTML = UI.skeleton(3);
|
||||
await _loadFilme();
|
||||
_renderMovieGrid(grid);
|
||||
});
|
||||
});
|
||||
|
||||
content.querySelector('#movies-sort')?.addEventListener('change', async e => {
|
||||
_sort = e.target.value;
|
||||
const grid = content.querySelector('#movie-grid');
|
||||
grid.innerHTML = UI.skeleton(3);
|
||||
await _loadFilme();
|
||||
_renderMovieGrid(grid);
|
||||
});
|
||||
|
||||
_renderMovieGrid(content.querySelector('#movie-grid'));
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +152,10 @@ window.Page_movies = (() => {
|
|||
|
||||
if (_filter === 'stirbt') list = list.filter(f => f.stirbt_der_hund);
|
||||
if (_filter === 'ueberlebt') list = list.filter(f => !f.stirbt_der_hund);
|
||||
if (_filter === 'top') list = list.filter(f => f.bewertung_avg >= 4.0);
|
||||
if (_filter === 'top') list = list.filter(f => (f.imdb_rating || 0) >= 7.5 || f.bewertung_avg >= 4.0);
|
||||
|
||||
const countEl = document.getElementById('movies-count');
|
||||
if (countEl) countEl.textContent = `${list.length} Einträge`;
|
||||
|
||||
if (list.length === 0) {
|
||||
grid.innerHTML = `<div style="grid-column:1/-1;padding:var(--space-8);text-align:center;color:var(--c-text-secondary)">Keine Filme für diesen Filter.</div>`;
|
||||
|
|
@ -130,18 +179,24 @@ window.Page_movies = (() => {
|
|||
function _movieCard(film) {
|
||||
const stirbt = film.stirbt_der_hund;
|
||||
const tag = stirbt
|
||||
? `<div class="movie-tag-stirbt"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg> ACHTUNG: Der Hund stirbt</div>`
|
||||
: `<div class="movie-tag-ueberlebt"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#check-circle"></use></svg> Der Hund überlebt</div>`;
|
||||
? `<div class="movie-tag-stirbt"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg> Hund stirbt</div>`
|
||||
: `<div class="movie-tag-ueberlebt"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#check-circle"></use></svg> Hund überlebt</div>`;
|
||||
const stars = _starsHtml(film.bewertung_avg, film.id, film.user_rating, false);
|
||||
const typLabel = film.typ === 'serie' ? '📺 Serie' : film.typ === 'doku' ? '🎥 Doku' : '';
|
||||
const imdb = film.imdb_rating ? `<span style="font-size:var(--text-xs);color:var(--c-text-muted)">IMDb ${film.imdb_rating}</span>` : '';
|
||||
const streaming = film.streaming ? `<span style="font-size:var(--text-xs);color:var(--c-text-muted)">${_esc(film.streaming)}</span>` : '';
|
||||
|
||||
return `
|
||||
<div class="movie-card" data-film-id="${_esc(film.id)}">
|
||||
<div class="movie-card-emoji">${film.bild_emoji}</div>
|
||||
<div class="movie-card-body">
|
||||
<div class="movie-card-title">${_esc(film.titel)} <span class="movie-card-year">(${film.jahr})</span></div>
|
||||
<div class="movie-card-genre">${_esc(film.genre)}</div>
|
||||
<div class="movie-card-genre" style="display:flex;gap:var(--space-2);align-items:center;flex-wrap:wrap">
|
||||
<span>${_esc(film.genre)}</span>${typLabel ? `<span style="font-size:var(--text-xs);color:var(--c-text-muted)">${typLabel}</span>` : ''}
|
||||
</div>
|
||||
<div class="movie-card-rasse"><svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#paw-print"></use></svg> ${_esc(film.hund_rasse)}</div>
|
||||
${tag}
|
||||
<div style="display:flex;gap:var(--space-3);margin-top:var(--space-1)">${imdb}${streaming}</div>
|
||||
<div class="movie-card-stars">${stars}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue