Feature: HdM Community-Vote — alle öffentlichen Hunde wählbar, eigene ausgenommen, SW by-v597
This commit is contained in:
parent
83958cbb0b
commit
031c6028ac
5 changed files with 127 additions and 51 deletions
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '596'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '597'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VERSION = '1.2.1'; // ← semantische Version, wird bei make release gesetzt
|
||||
const IS_STAGING = location.hostname === 'staging.banyaro.app';
|
||||
|
||||
|
|
|
|||
|
|
@ -232,45 +232,12 @@ window.Page_forum = (() => {
|
|||
}
|
||||
|
||||
async function _openHdmModal(data) {
|
||||
// Immer frische Daten laden
|
||||
try { data = await API.get('/movies/hund-des-monats'); } catch { /* nutze gecachte */ }
|
||||
try { data = await API.get('/movies/hund-des-monats'); } catch { /* gecachte Daten */ }
|
||||
|
||||
const [year, month] = data.monat.split('-');
|
||||
const monthName = new Intl.DateTimeFormat('de-DE', { month: 'long', year: 'numeric' })
|
||||
.format(new Date(+year, +month - 1, 1));
|
||||
|
||||
let voteSection = '';
|
||||
if (_appState.user && _appState.dogs?.length > 0) {
|
||||
const cards = _appState.dogs.map(dog => {
|
||||
const isVoted = data.user_vote === dog.id;
|
||||
const av = dog.foto_url
|
||||
? `<img src="${_esc(dog.foto_url)}" alt="${_esc(dog.name)}" class="hdm-vote-av-img">`
|
||||
: `<span class="hdm-vote-av-placeholder">${_esc(dog.name.charAt(0).toUpperCase())}</span>`;
|
||||
return `
|
||||
<div class="hdm-vote-card${isVoted ? ' hdm-vote-card--voted' : ''}" data-dog-id="${dog.id}">
|
||||
<div class="hdm-vote-av">${av}</div>
|
||||
<div class="hdm-vote-name">${_esc(dog.name)}</div>
|
||||
${dog.rasse ? `<div class="hdm-vote-rasse">${_esc(dog.rasse)}</div>` : ''}
|
||||
<button class="btn${isVoted ? ' btn-primary' : ' btn-secondary'} hdm-vote-btn" data-dog-id="${dog.id}">
|
||||
${isVoted ? `${UI.icon('check-circle')} Gewählt` : 'Abstimmen'}
|
||||
</button>
|
||||
</div>`;
|
||||
}).join('');
|
||||
voteSection = `
|
||||
<div class="hdm-section">
|
||||
<h3 class="hdm-section-title">Für welchen deiner Hunde möchtest du abstimmen?</h3>
|
||||
<div class="hdm-vote-grid" id="hdm-vote-grid">${cards}</div>
|
||||
</div>`;
|
||||
} else if (!_appState.user) {
|
||||
voteSection = `
|
||||
<div class="hdm-section">
|
||||
<p style="color:var(--c-text-secondary);font-size:var(--text-sm)">
|
||||
<a href="#" id="hdm-login-link" style="color:var(--c-primary);font-weight:var(--weight-semibold)">Anmelden</a>
|
||||
um für deinen Hund abzustimmen.
|
||||
</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const topList = data.top?.length
|
||||
? data.top.slice(0, 5).map((dog, i) => {
|
||||
const medal = ['🥇','🥈','🥉','4️⃣','5️⃣'][i];
|
||||
|
|
@ -290,7 +257,26 @@ window.Page_forum = (() => {
|
|||
<div class="hdm-top-stimmen">${dog.stimmen} ${UI.icon('star')}</div>
|
||||
</div>`;
|
||||
}).join('')
|
||||
: `<p style="color:var(--c-text-secondary);padding:var(--space-4)">Noch keine Stimmen diesen Monat. Sei der Erste!</p>`;
|
||||
: `<p style="color:var(--c-text-secondary);padding:var(--space-4)">Noch keine Stimmen. Sei der Erste!</p>`;
|
||||
|
||||
const voteHint = !_appState.user
|
||||
? `<div class="hdm-section">
|
||||
<p style="color:var(--c-text-secondary);font-size:var(--text-sm)">
|
||||
<a href="#" id="hdm-login-link" style="color:var(--c-primary);font-weight:var(--weight-semibold)">Anmelden</a>
|
||||
um abstimmen zu können.
|
||||
</p>
|
||||
</div>`
|
||||
: `<div class="hdm-section">
|
||||
<h3 class="hdm-section-title">Für welchen Hund möchtest du abstimmen?</h3>
|
||||
<div class="hdm-kandidaten-search">
|
||||
<input type="search" id="hdm-search" class="form-control"
|
||||
placeholder="Name oder Rasse suchen …" autocomplete="off"
|
||||
style="font-size:var(--text-sm)">
|
||||
</div>
|
||||
<div id="hdm-kandidaten-grid" class="hdm-vote-grid">
|
||||
${UI.skeleton(3)}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
const body = `
|
||||
<div class="hdm-header">
|
||||
|
|
@ -298,7 +284,7 @@ window.Page_forum = (() => {
|
|||
<h2 class="hdm-title">Hund des Monats</h2>
|
||||
<div class="hdm-monat">${_esc(monthName)}</div>
|
||||
</div>
|
||||
${voteSection}
|
||||
${voteHint}
|
||||
<div class="hdm-section">
|
||||
<h3 class="hdm-section-title">Top 5 diesen Monat</h3>
|
||||
<div id="hdm-top-list">${topList}</div>
|
||||
|
|
@ -311,20 +297,72 @@ window.Page_forum = (() => {
|
|||
e.preventDefault(); UI.modal.close(); App.navigate('settings');
|
||||
});
|
||||
|
||||
document.querySelectorAll('.hdm-vote-btn').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const dogId = parseInt(btn.dataset.dogId);
|
||||
await UI.asyncButton(btn, async () => {
|
||||
try {
|
||||
await API.post('/movies/hund-des-monats/vote', { dog_id: dogId });
|
||||
UI.toast.success('Stimme abgegeben!');
|
||||
UI.modal.close();
|
||||
_loadHdmCard();
|
||||
} catch (err) {
|
||||
UI.toast.error(err.message || 'Fehler beim Abstimmen.');
|
||||
}
|
||||
if (!_appState.user) return;
|
||||
|
||||
// Kandidaten laden und rendern
|
||||
let _kandidaten = [];
|
||||
const _renderKandidaten = (list) => {
|
||||
const grid = document.getElementById('hdm-kandidaten-grid');
|
||||
if (!grid) return;
|
||||
if (!list.length) {
|
||||
grid.innerHTML = `<p style="color:var(--c-text-secondary);font-size:var(--text-sm);padding:var(--space-3) 0">Keine Hunde gefunden.</p>`;
|
||||
return;
|
||||
}
|
||||
grid.innerHTML = list.map(dog => {
|
||||
const isVoted = data.user_vote === dog.id;
|
||||
const av = dog.foto_url
|
||||
? `<img src="${_esc(dog.foto_url)}" alt="${_esc(dog.name)}" class="hdm-vote-av-img">`
|
||||
: `<span class="hdm-vote-av-placeholder">${_esc(dog.name.charAt(0).toUpperCase())}</span>`;
|
||||
const vorname = dog.besitzer_name ? _esc(dog.besitzer_name.split(' ')[0]) : '';
|
||||
return `
|
||||
<div class="hdm-vote-card${isVoted ? ' hdm-vote-card--voted' : ''}">
|
||||
<div class="hdm-vote-av">${av}</div>
|
||||
<div class="hdm-vote-name">${_esc(dog.name)}</div>
|
||||
${dog.rasse ? `<div class="hdm-vote-rasse">${_esc(dog.rasse)}</div>` : ''}
|
||||
${vorname ? `<div class="hdm-vote-besitzer" style="font-size:var(--text-xs);color:var(--c-text-muted)">von ${vorname}</div>` : ''}
|
||||
${dog.stimmen > 0 ? `<div style="font-size:var(--text-xs);color:var(--c-text-muted)">${dog.stimmen} ${UI.icon('star')}</div>` : ''}
|
||||
<button class="btn btn-sm ${isVoted ? 'btn-primary' : 'btn-secondary'} hdm-vote-btn"
|
||||
data-dog-id="${dog.id}" ${isVoted ? 'disabled' : ''}>
|
||||
${isVoted ? `${UI.icon('check-circle')} Gewählt` : 'Abstimmen'}
|
||||
</button>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
grid.querySelectorAll('.hdm-vote-btn:not([disabled])').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const dogId = parseInt(btn.dataset.dogId);
|
||||
await UI.asyncButton(btn, async () => {
|
||||
try {
|
||||
await API.post('/movies/hund-des-monats/vote', { dog_id: dogId });
|
||||
data.user_vote = dogId;
|
||||
UI.toast.success('Stimme abgegeben!');
|
||||
UI.modal.close();
|
||||
_loadHdmCard();
|
||||
} catch (err) {
|
||||
UI.toast.error(err.message || 'Fehler beim Abstimmen.');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
_kandidaten = await API.get('/movies/hund-des-monats/kandidaten');
|
||||
} catch {
|
||||
document.getElementById('hdm-kandidaten-grid').innerHTML =
|
||||
`<p style="color:var(--c-danger);font-size:var(--text-sm)">Kandidaten konnten nicht geladen werden.</p>`;
|
||||
return;
|
||||
}
|
||||
_renderKandidaten(_kandidaten);
|
||||
|
||||
document.getElementById('hdm-search')?.addEventListener('input', e => {
|
||||
const q = e.target.value.trim().toLowerCase();
|
||||
_renderKandidaten(q
|
||||
? _kandidaten.filter(d =>
|
||||
(d.name || '').toLowerCase().includes(q) ||
|
||||
(d.rasse || '').toLowerCase().includes(q))
|
||||
: _kandidaten
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue