Icons: Emoji/Unicode → Phosphor in 8 Dateien + 16 neue Sprite-Icons — SW by-v415

Sprite: arrow-up/down, bug, check-circle, ear, fish, flask, flower,
        medal, question, scissors, tent, ticket, tooth, trend-up/down

poison.js:     TYPEN (🎣☠️⚗️⚠️) → question/fish/skull/flask/warning
events.js:     TYPEN (🎪🏆🎓🐕🛍️🥇📌) → ticket/trophy/graduation-cap/dog/shopping-bag/medal/push-pin
dog-profile.js: Pflege-Emojis (✂️💅🦷👂👁🐾🦟🌸❤️🐶) → Phosphor
trainingsplaene.js: (🐶🐕🦮) → dog mit SVG-Icon
health.js:     ▲▼→ → trend-up/trend-down/arrow-right
uebungen.js:   ↑↓→★ → trend-up/trend-down/arrow-right/star
admin.js:      ✓✗ → check/x in HTML-Templates
moderation.js: ✓✗ → check/x Buttons
This commit is contained in:
rene 2026-04-26 09:26:13 +02:00
parent f6586c88ee
commit fd76eddfb9
11 changed files with 81 additions and 43 deletions

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '393'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '394'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const App = (() => {

View file

@ -1095,11 +1095,11 @@ window.Page_admin = (() => {
<td class="adm-td" style="font-weight:var(--weight-semibold)">${_esc(z.rasse_slug)}</td>
<td class="adm-td">${_esc(z.name)}${z.zwingername ? `<br><span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(z.zwingername)}</span>` : ''}</td>
<td class="adm-td">${_esc([z.plz, z.ort, z.bundesland].filter(Boolean).join(' '))}</td>
<td class="adm-td">${z.vdh_mitglied ? '<span style="color:var(--c-success)">✓ VDH</span>' : '—'}</td>
<td class="adm-td">${z.vdh_mitglied ? `<span style="color:var(--c-success);display:flex;align-items:center;gap:2px"><svg class="ph-icon" style="width:14px;height:14px" aria-hidden="true"><use href="/icons/phosphor.svg#check"></use></svg> VDH</span>` : '—'}</td>
<td class="adm-td">${z.website ? `<a href="${_esc(z.website)}" target="_blank" style="color:var(--c-primary);font-size:var(--text-xs)">Link</a>` : '—'}</td>
<td class="adm-td" style="text-align:right;white-space:nowrap">
<button class="btn btn-sm btn-primary adm-zuchter-approve" data-id="${z.id}" style="margin-right:4px"> Freigeben</button>
<button class="btn btn-sm btn-ghost adm-zuchter-delete" data-id="${z.id}" style="color:var(--c-danger)"></button>
<button class="btn btn-sm btn-primary adm-zuchter-approve" data-id="${z.id}" style="margin-right:4px"><svg class="ph-icon" style="width:14px;height:14px" aria-hidden="true"><use href="/icons/phosphor.svg#check"></use></svg> Freigeben</button>
<button class="btn btn-sm btn-ghost adm-zuchter-delete" data-id="${z.id}" style="color:var(--c-danger)"><svg class="ph-icon" style="width:14px;height:14px" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg></button>
</td>
</tr>`).join('')}
</tbody></table></div></div>`;

View file

@ -347,10 +347,13 @@ window.Page_dog_profile = (() => {
if (!data?.tipps?.length) return;
const t = data.tipp_des_tages;
const _ph = n => `<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#${n}"></use></svg>`;
const kat_icons = {
'Fell':'✂️','Krallen':'💅','Zähne':'🦷','Ohren':'👂',
'Augen':'👁','Pfoten':'🐾','Parasiten':'🦟',
'Saisonal':'🌸','Gesundheitsvorsorge':'❤️','Welpen-Pflege':'🐶',
'Fell': _ph('scissors'), 'Krallen': _ph('scissors'),
'Zähne': _ph('tooth'), 'Ohren': _ph('ear'),
'Augen': _ph('eye'), 'Pfoten': _ph('paw-print'),
'Parasiten': _ph('bug'), 'Saisonal': _ph('flower'),
'Gesundheitsvorsorge':_ph('heart'), 'Welpen-Pflege': _ph('dog'),
};
const pflegeArtBadge = data.fell_pflege_art === 'schneiden'
@ -381,7 +384,7 @@ window.Page_dog_profile = (() => {
${t.saisonal_aktuell ? '🌸 Aktuell & Saisonal' : '💡 Tipp des Tages'}
</div>
<div style="font-weight:600;font-size:var(--text-sm);margin-bottom:4px">
${kat_icons[t.kategorie]||'🐾'} ${_esc(t.titel)}
${kat_icons[t.kategorie]||_ph('paw-print')} ${_esc(t.titel)}
</div>
<div style="font-size:12px;color:var(--c-text-secondary);margin-bottom:8px;
line-height:1.5">${_esc(t.beschreibung||'')}</div>
@ -415,7 +418,7 @@ window.Page_dog_profile = (() => {
<div style="margin-bottom:var(--space-3)">
<div style="font-size:11px;font-weight:700;color:var(--c-text-muted);
text-transform:uppercase;margin-bottom:8px;display:flex;align-items:center">
${kat_icons[kat]||'🐾'} ${_esc(kat)}${katBadge}</div>
${kat_icons[kat]||_ph('paw-print')} ${_esc(kat)}${katBadge}</div>
${katTipps.map(tip => `
<details style="background:var(--c-surface-2);border-radius:8px;
padding:10px;margin-bottom:6px">

View file

@ -9,13 +9,13 @@ window.Page_events = (() => {
// Konstanten
// ----------------------------------------------------------
const TYPEN = [
{ id: 'alle', label: 'Alle', icon: '🎪' },
{ id: 'ausstellung', label: 'Ausstellung', icon: '🏆' },
{ id: 'training', label: 'Training', icon: '🎓' },
{ id: 'treffen', label: 'Treffen', icon: '🐕' },
{ id: 'markt', label: 'Markt', icon: '🛍️' },
{ id: 'wettkampf', label: 'Wettkampf', icon: '🥇' },
{ id: 'sonstiges', label: 'Sonstiges', icon: '📌' },
{ id: 'alle', label: 'Alle', icon: 'ticket' },
{ id: 'ausstellung', label: 'Ausstellung', icon: 'trophy' },
{ id: 'training', label: 'Training', icon: 'graduation-cap'},
{ id: 'treffen', label: 'Treffen', icon: 'dog' },
{ id: 'markt', label: 'Markt', icon: 'shopping-bag' },
{ id: 'wettkampf', label: 'Wettkampf', icon: 'medal' },
{ id: 'sonstiges', label: 'Sonstiges', icon: 'push-pin' },
];
const TYP_COLOR = {
@ -88,7 +88,7 @@ window.Page_events = (() => {
<div class="events-filter-bar by-tabs" id="ev-filter-bar">
${TYPEN.map(t => `
<button class="by-tab ${t.id === 'alle' ? 'active' : ''}" data-ev-typ="${t.id}">
${t.icon} ${t.label}
${UI.icon(t.icon)} ${t.label}
</button>
`).join('')}
</div>
@ -215,7 +215,7 @@ window.Page_events = (() => {
${isVdh ? `<span class="ev-vdh-badge" title="Vom VDH importiert">VDH</span>` : ''}
</div>
<div class="events-card-meta">
<span class="events-badge" style="background:${color}20;color:${color}">${typ.icon} ${typ.label}</span>
<span class="events-badge" style="background:${color}20;color:${color}">${UI.icon(typ.icon)} ${typ.label}</span>
${ev.uhrzeit ? `· ${_icon('clock')} ${ev.uhrzeit} Uhr` : ''}
${ev.ort_name ? `· ${_icon('map-pin')} ${UI.escape(ev.ort_name)}` : ''}
</div>
@ -269,7 +269,7 @@ window.Page_events = (() => {
// Events nutzen rotierten Diamant-Marker (nicht Kreis) — UI.leafletMarker() nicht anwendbar
const icon = L.divIcon({
className: '',
html: `<div style="width:32px;height:32px;border-radius:50% 50% 50% 0;background:${color};border:2px solid #fff;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 6px rgba(0,0,0,0.3);transform:rotate(-45deg)"><span style="transform:rotate(45deg)">${typ.icon}</span></div>`,
html: `<div style="width:32px;height:32px;border-radius:50% 50% 50% 0;background:${color};border:2px solid #fff;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 6px rgba(0,0,0,0.3);transform:rotate(-45deg);color:#fff"><svg style="width:14px;height:14px;transform:rotate(45deg);fill:currentColor" viewBox="0 0 256 256"><use href="/icons/phosphor.svg#${typ.icon}"></use></svg></div>`,
iconSize: [32, 32], iconAnchor: [16, 32],
});
const popup = `
@ -338,7 +338,7 @@ window.Page_events = (() => {
const body = `
<div style="display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-3)">
<span class="events-badge" style="background:${color}20;color:${color};font-size:var(--text-sm)">${typ.icon} ${typ.label}</span>
<span class="events-badge" style="background:${color}20;color:${color};font-size:var(--text-sm)">${UI.icon(typ.icon)} ${typ.label}</span>
${isVdh ? `<span class="ev-vdh-badge">VDH</span>` : ''}
</div>
<div class="events-detail-row">${_icon('calendar-dots')} ${datum}${ev.uhrzeit ? ' · ' + ev.uhrzeit + ' Uhr' : ''}</div>
@ -484,7 +484,7 @@ window.Page_events = (() => {
<label class="form-label">Typ *</label>
<select class="form-control" name="typ">
${TYPEN.filter(t => t.id !== 'alle').map(t =>
`<option value="${t.id}" ${ev?.typ === t.id ? 'selected' : ''}>${t.icon} ${t.label}</option>`
`<option value="${t.id}" ${ev?.typ === t.id ? 'selected' : ''}>${UI.icon(t.icon)} ${t.label}</option>`
).join('')}
</select>
</div>

View file

@ -487,7 +487,7 @@ window.Page_health = (() => {
const deltaHtml = delta !== null ? (() => {
const sign = delta > 0 ? '+' : '';
const color = delta > 0 ? 'var(--c-warning)' : delta < 0 ? 'var(--c-success)' : 'var(--c-text-muted)';
const arrow = delta > 0 ? '▲' : delta < 0 ? '▼' : '→';
const arrow = delta > 0 ? '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trend-up"></use></svg>' : delta < 0 ? '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trend-down"></use></svg>' : '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#arrow-right"></use></svg>';
return `<div style="font-size:var(--text-sm);color:${color};margin-top:var(--space-1)">
${arrow} ${sign}${delta.toFixed(1)} kg seit letzter Messung
</div>`;

View file

@ -194,9 +194,9 @@ window.Page_moderation = (() => {
margin-bottom:var(--space-3)">Noch kein Foto vorhanden</div>`}
<div style="display:flex;gap:var(--space-2)">
<button class="btn btn-sm btn-primary mod-foto-approve"
data-id="${f.id}" style="flex:1"> Freigeben</button>
data-id="${f.id}" style="flex:1"><svg class="ph-icon" style="width:14px;height:14px" aria-hidden="true"><use href="/icons/phosphor.svg#check"></use></svg> Freigeben</button>
<button class="btn btn-sm btn-ghost mod-foto-reject"
data-id="${f.id}" style="color:var(--c-danger)"> Ablehnen</button>
data-id="${f.id}" style="color:var(--c-danger)"><svg class="ph-icon" style="width:14px;height:14px" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg> Ablehnen</button>
</div>
</div>
`).join('')}

View file

@ -17,11 +17,11 @@ window.Page_poison = (() => {
let _userPos = null;
const TYPEN = {
unbekannt: { label: 'Unbekannt', icon: '', color: '#e67e22' },
koeoder: { label: 'Köder', icon: '🎣', color: '#e74c3c' },
vergiftet: { label: 'Vergiftetes Tier', icon: '☠️', color: '#8e44ad' },
chemikalie: { label: 'Chemikalie', icon: '⚗️', color: '#c0392b' },
andere: { label: 'Andere Gefahr', icon: '⚠️', color: '#d35400' },
unbekannt: { label: 'Unbekannt', icon: 'question', color: '#e67e22' },
koeoder: { label: 'Köder', icon: 'fish', color: '#e74c3c' },
vergiftet: { label: 'Vergiftetes Tier', icon: 'skull', color: '#8e44ad' },
chemikalie: { label: 'Chemikalie', icon: 'flask', color: '#c0392b' },
andere: { label: 'Andere Gefahr', icon: 'warning', color: '#d35400' },
};
// ----------------------------------------------------------
@ -199,10 +199,10 @@ window.Page_poison = (() => {
? `${r.distanz_m} m`
: `${(r.distanz_m / 1000).toFixed(1)} km`;
const marker = UI.leafletMarker({ lat: r.lat, lon: r.lon, color: typ.color, icon: typ.icon, size: 34 })
const marker = UI.leafletMarker({ lat: r.lat, lon: r.lon, color: typ.color, icon: UI.icon(typ.icon), size: 34 })
.addTo(_map)
.bindPopup(`
<b>${typ.icon} ${typ.label}</b><br>
<b>${UI.icon(typ.icon)} ${typ.label}</b><br>
${r.beschreibung ? UI.escape(r.beschreibung.slice(0, 80)) + '<br>' : ''}
<small>📍 ${distStr} entfernt</small><br>
<small>📅 ${_fmtDate(r.created_at)}</small>
@ -253,7 +253,7 @@ window.Page_poison = (() => {
style="cursor:pointer;margin-bottom:var(--space-3);
border-left:4px solid ${typ.color}">
<div style="display:flex;gap:var(--space-3);align-items:flex-start">
<div style="font-size:2rem;line-height:1;flex-shrink:0">${typ.icon}</div>
<div style="width:40px;height:40px;flex-shrink:0;color:${typ.color};display:flex;align-items:center;justify-content:center">${UI.icon(typ.icon)}</div>
<div style="flex:1;min-width:0">
<div style="display:flex;align-items:center;gap:var(--space-2);
margin-bottom:var(--space-1);flex-wrap:wrap">
@ -305,7 +305,7 @@ window.Page_poison = (() => {
<div style="display:flex;gap:var(--space-2);flex-wrap:wrap;margin-bottom:var(--space-3)">
<span class="badge" style="background:${typ.color};color:#fff">
${typ.icon} ${typ.label}
${UI.icon(typ.icon)} ${typ.label}
</span>
${r.bestaetigt ? '<span class="badge badge-success">✅ Bestätigt</span>' : ''}
</div>
@ -333,7 +333,7 @@ window.Page_poison = (() => {
</div>
`;
UI.modal.open({ title: `${typ.icon} Giftköder-Meldung`, body });
UI.modal.open({ title: `${UI.icon(typ.icon)} Giftköder-Meldung`, body });
document.getElementById('detail-confirm')?.addEventListener('click', async () => {
try {
@ -423,8 +423,8 @@ window.Page_poison = (() => {
}
const typOpts = Object.entries(TYPEN)
.map(([val, { icon, label }]) =>
`<option value="${val}">${icon} ${label}</option>`)
.map(([val, { label }]) =>
`<option value="${val}">${label}</option>`)
.join('');
const body = `

View file

@ -130,15 +130,16 @@ window.Page_trainingsplaene = (() => {
// ----------------------------------------------------------
function _renderPlanSelector() {
const plans = [
{ id: 'welpe', label: '🐶 Welpe', sub: '06 Monate' },
{ id: 'junior', label: '🐕 Junior', sub: '618 Monate' },
{ id: 'erwachsen',label: '🦮 Erwachsener Hund', sub: 'Grund- & Aufbaukurs' },
{ id: 'welpe', label: 'Welpe', icon: 'dog', sub: '06 Monate' },
{ id: 'junior', label: 'Junior', icon: 'dog', sub: '618 Monate' },
{ id: 'erwachsen',label: 'Erwachsener Hund', icon: 'dog', sub: 'Grund- & Aufbaukurs' },
];
const btns = plans.map(p => `
<button class="by-tab${_activePlan === p.id ? ' active' : ''}" data-plan="${p.id}"
style="flex:1;min-width:90px;display:flex;flex-direction:column;align-items:center;
justify-content:center;gap:2px;white-space:normal;text-align:center;line-height:1.2">
<span style="font-size:1.1rem">${p.label}</span>
<svg class="ph-icon" aria-hidden="true" style="width:22px;height:22px"><use href="/icons/phosphor.svg#${p.icon}"></use></svg>
<span style="font-size:var(--text-sm);font-weight:600">${p.label}</span>
<span style="font-size:var(--text-xs);opacity:0.8">${_esc(p.sub)}</span>
</button>`).join('');
return `<div style="display:flex;gap:var(--space-2);margin-bottom:var(--space-4);flex-wrap:wrap">${btns}</div>`;

View file

@ -606,7 +606,8 @@ window.Page_uebungen = (() => {
'entdecken': { label: 'Auffrischen',bg: 'var(--c-primary-subtle)', border: 'var(--c-primary-light)', text: 'var(--c-primary)', icon: 'clock' },
'levelup': { label: 'Nächster Level', bg: 'rgba(234,179,8,0.10)', border: 'rgba(234,179,8,0.30)', text: '#facc15', icon: 'graduation-cap' },
};
const TREND_ICON = { improving: '↑', declining: '↓', stable: '→', new: '★' };
const _phSm = n => `<svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#${n}"></use></svg>`;
const TREND_ICON = { improving: _phSm('trend-up'), declining: _phSm('trend-down'), stable: _phSm('arrow-right'), new: _phSm('star') };
const TREND_COLOR = { improving: 'var(--c-success,#16a34a)', declining: '#dc2626', stable: 'var(--c-text-secondary)', new: 'var(--c-primary)' };
const cards = recs.map((r, i) => {
@ -918,7 +919,8 @@ window.Page_uebungen = (() => {
const color = avg >= 75 ? '#15803d' : avg >= 50 ? '#c2410c' : '#dc2626';
const bg = avg >= 75 ? 'rgba(22,163,74,0.10)' : avg >= 50 ? 'rgba(234,88,12,0.10)' : 'rgba(220,38,38,0.10)';
const border = avg >= 75 ? 'rgba(22,163,74,0.30)' : avg >= 50 ? 'rgba(234,88,12,0.30)' : 'rgba(220,38,38,0.30)';
const arrow = { improving: '↑', declining: '↓', stable: '→', new: '' }[stat.trend] || '';
const _phXs = n => `<svg class="ph-icon" aria-hidden="true" style="width:12px;height:12px"><use href="/icons/phosphor.svg#${n}"></use></svg>`;
const arrow = { improving: _phXs('trend-up'), declining: _phXs('trend-down'), stable: _phXs('arrow-right'), new: '' }[stat.trend] || '';
return `
<span style="font-size:9px;font-weight:600;padding:1px 6px;border-radius:999px;
background:${bg};color:${color};border:1px solid ${border};white-space:nowrap">