188 lines
7.1 KiB
JavaScript
188 lines
7.1 KiB
JavaScript
/* ============================================================
|
|
BAN YARO — Tierfutter-Rückrufe
|
|
Seiten-Modul: RASFF EU Rückruf-Alarm für Heimtierfutter.
|
|
============================================================ */
|
|
|
|
window.Page_recalls = (() => {
|
|
|
|
// ----------------------------------------------------------
|
|
// MODUL-STATE
|
|
// ----------------------------------------------------------
|
|
let _container = null;
|
|
let _appState = null;
|
|
let _recalls = [];
|
|
let _query = '';
|
|
|
|
// ----------------------------------------------------------
|
|
// INIT
|
|
// ----------------------------------------------------------
|
|
async function init(container, appState) {
|
|
_container = container;
|
|
_appState = appState;
|
|
_query = '';
|
|
await _render();
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// REFRESH
|
|
// ----------------------------------------------------------
|
|
async function refresh() {
|
|
_recalls = [];
|
|
_query = '';
|
|
await _render();
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// RENDER
|
|
// ----------------------------------------------------------
|
|
async function _render() {
|
|
_container.innerHTML = `
|
|
<!-- Warnbanner -->
|
|
<div class="recalls-warning-banner">
|
|
<svg class="ph-icon recalls-warning-icon" aria-hidden="true">
|
|
<use href="/icons/phosphor.svg#warning"></use>
|
|
</svg>
|
|
<p class="recalls-warning-text">
|
|
<strong>Hinweis:</strong> Prüfe immer das Mindesthaltbarkeitsdatum und die Chargen-Nummer
|
|
bevor du ein gemeldetes Produkt entsorgst oder zurückgibst.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Suchfeld -->
|
|
<div style="position:relative;margin-bottom:var(--space-4)">
|
|
<svg class="ph-icon" aria-hidden="true"
|
|
style="position:absolute;left:var(--space-3);top:50%;transform:translateY(-50%);
|
|
color:var(--c-text-muted);pointer-events:none">
|
|
<use href="/icons/phosphor.svg#magnifying-glass"></use>
|
|
</svg>
|
|
<input type="search" id="recalls-search" placeholder="Produkt, Gefahr oder Herkunft suchen…"
|
|
value="${UI.escape(_query)}"
|
|
style="width:100%;padding:var(--space-2) var(--space-3) var(--space-2) calc(var(--space-3)*2 + 1.2rem);
|
|
border:1px solid var(--c-border);border-radius:var(--radius-md);
|
|
font-size:var(--text-sm);background:var(--c-surface);color:var(--c-text);
|
|
box-sizing:border-box">
|
|
</div>
|
|
|
|
<!-- Ergebnis-Liste -->
|
|
<div id="recalls-list">${UI.skeleton(4)}</div>
|
|
`;
|
|
|
|
// Suchfeld-Handler
|
|
_container.querySelector('#recalls-search').addEventListener('input', (e) => {
|
|
_query = e.target.value.trim();
|
|
_renderList();
|
|
});
|
|
|
|
await _loadRecalls();
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// DATEN LADEN
|
|
// ----------------------------------------------------------
|
|
async function _loadRecalls() {
|
|
try {
|
|
const url = _query ? `/recalls?q=${encodeURIComponent(_query)}` : '/recalls';
|
|
_recalls = await API.get(url);
|
|
} catch {
|
|
_container.querySelector('#recalls-list').innerHTML = UI.emptyState({
|
|
icon: 'warning-circle',
|
|
title: 'Rückrufe konnten nicht geladen werden',
|
|
text: 'Bitte versuche es später erneut.',
|
|
});
|
|
return;
|
|
}
|
|
_renderList();
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// LISTE RENDERN
|
|
// ----------------------------------------------------------
|
|
function _renderList() {
|
|
const listEl = _container.querySelector('#recalls-list');
|
|
if (!listEl) return;
|
|
|
|
const filtered = _query
|
|
? _recalls.filter(r => {
|
|
const q = _query.toLowerCase();
|
|
return (r.titel || '').toLowerCase().includes(q)
|
|
|| (r.produkt || '').toLowerCase().includes(q)
|
|
|| (r.gefahr || '').toLowerCase().includes(q)
|
|
|| (r.herkunft || '').toLowerCase().includes(q);
|
|
})
|
|
: _recalls;
|
|
|
|
if (!filtered.length) {
|
|
const today = new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
|
listEl.innerHTML = UI.emptyState({
|
|
icon: 'check-circle',
|
|
title: 'Aktuell keine Rückrufe',
|
|
text: `Letzte Prüfung: ${today}`,
|
|
});
|
|
return;
|
|
}
|
|
|
|
listEl.innerHTML = filtered.map(r => _cardHtml(r)).join('');
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// EINZELNE KARTE
|
|
// ----------------------------------------------------------
|
|
function _cardHtml(r) {
|
|
const datum = r.datum
|
|
? new Date(r.datum).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })
|
|
: '';
|
|
|
|
const meta = [
|
|
r.herkunft ? `<span>${UI.icon('globe-hemisphere-west')} ${UI.escape(r.herkunft)}</span>` : '',
|
|
datum ? `<span>${UI.icon('calendar-blank')} ${datum}</span>` : '',
|
|
r.quelle ? `<span style="text-transform:uppercase;font-size:var(--text-xs);color:var(--c-text-muted)">${UI.escape(r.quelle)}</span>` : '',
|
|
].filter(Boolean).join('<span style="color:var(--c-border)"> · </span>');
|
|
|
|
const linkHtml = r.url
|
|
? `<a href="${UI.escape(r.url)}" target="_blank" rel="noopener"
|
|
style="display:inline-flex;align-items:center;gap:4px;font-size:var(--text-sm);
|
|
color:var(--c-primary);text-decoration:none;margin-top:var(--space-1)">
|
|
${UI.icon('arrow-square-out')} Details auf RASFF
|
|
</a>`
|
|
: '';
|
|
|
|
return `
|
|
<div style="background:var(--c-surface);border:1px solid var(--c-border);
|
|
border-left:4px solid #dc2626;border-radius:var(--radius-md);
|
|
padding:var(--space-3) var(--space-4);margin-bottom:var(--space-3)">
|
|
<!-- Titel -->
|
|
<div style="display:flex;align-items:flex-start;gap:var(--space-2);margin-bottom:var(--space-1)">
|
|
<svg class="ph-icon" aria-hidden="true" style="color:#dc2626;flex-shrink:0;margin-top:2px">
|
|
<use href="/icons/phosphor.svg#warning-octagon"></use>
|
|
</svg>
|
|
<strong style="font-size:var(--text-base);color:var(--c-text);line-height:1.4">
|
|
${UI.escape(r.produkt || r.titel)}
|
|
</strong>
|
|
</div>
|
|
|
|
<!-- Gefahr -->
|
|
${r.gefahr ? `
|
|
<p style="margin:0 0 var(--space-2) 0;font-size:var(--text-sm);color:var(--c-text-muted);
|
|
padding-left:calc(var(--space-2) + 1.2rem)">
|
|
${UI.escape(r.gefahr)}
|
|
</p>` : ''}
|
|
|
|
<!-- Meta-Zeile -->
|
|
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);
|
|
font-size:var(--text-sm);color:var(--c-text-muted);
|
|
padding-left:calc(var(--space-2) + 1.2rem)">
|
|
${meta}
|
|
</div>
|
|
|
|
<!-- Link -->
|
|
${linkHtml ? `<div style="padding-left:calc(var(--space-2) + 1.2rem)">${linkHtml}</div>` : ''}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// PUBLIC API
|
|
// ----------------------------------------------------------
|
|
return { init, refresh };
|
|
|
|
})();
|