Feature: UX-Fixes — Zahnrad weg, POI-Kombi-Typen, exp-fab-Position, Welten-Config in DB (SW by-v653)
- worlds-settings Zahnrad komplett entfernt (war auf Mobile sichtbar, auf Desktop schon hidden) - exp-fab: bottom jetzt calc(--nav-bottom-height + --safe-bottom + --space-2) — kein Overlap mit worlds-back auf iPhone - Karte POI: neue Typen bank, bank_kotbeutel, bank_kotbeutel_abfall, kotbeutel_abfall (Backend + Frontend) - Welten-Chip-Config: GET/PUT /profile/world-config, Spalte users.world_config TEXT (Migration), Sync bei Init + Speichern
This commit is contained in:
parent
f0b5e6e89b
commit
1fdba57365
9 changed files with 84 additions and 27 deletions
|
|
@ -1923,6 +1923,11 @@ def _migrate(conn_factory):
|
|||
)
|
||||
""")
|
||||
|
||||
# Welten-Chip-Konfiguration pro User
|
||||
existing_u = [row[1] for row in conn.execute("PRAGMA table_info(users)").fetchall()]
|
||||
if 'world_config' not in existing_u:
|
||||
conn.execute("ALTER TABLE users ADD COLUMN world_config TEXT")
|
||||
|
||||
# Wiederkehrende Ausgaben (Daueraufträge)
|
||||
conn.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS recurring_expenses (
|
||||
|
|
|
|||
|
|
@ -279,11 +279,15 @@ class UserPoiIn(BaseModel):
|
|||
|
||||
ALLOWED_TYPES = {
|
||||
'waste_basket', 'drinking_water', 'dog_park',
|
||||
'giftkoeder', # Giftköder-Meldung (Community-Pin mit Radius)
|
||||
'kotbeutel', # Kotbeutelspender
|
||||
'gefahr', # Allgemeine Gefahr / Hinweis
|
||||
'parkplatz', # Hundefreundlicher Parkplatz
|
||||
'treffpunkt', # Treffpunkt für Hundehalter
|
||||
'giftkoeder', # Giftköder-Meldung (Community-Pin mit Radius)
|
||||
'kotbeutel', # Kotbeutelspender
|
||||
'kotbeutel_abfall', # Kotbeutelspender + Mülleimer Kombi
|
||||
'bank', # Sitzbank
|
||||
'bank_kotbeutel', # Sitzbank + Kotbeutelspender
|
||||
'bank_kotbeutel_abfall', # Sitzbank + Kotbeutelspender + Mülleimer
|
||||
'gefahr', # Allgemeine Gefahr / Hinweis
|
||||
'parkplatz', # Hundefreundlicher Parkplatz
|
||||
'treffpunkt', # Treffpunkt für Hundehalter
|
||||
'sonstiges',
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,3 +113,28 @@ async def upload_avatar(
|
|||
)
|
||||
|
||||
return {"avatar_url": avatar_url}
|
||||
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# GET /profile/world-config — Welten-Chip-Konfiguration laden
|
||||
# PUT /profile/world-config — Welten-Chip-Konfiguration speichern
|
||||
# ----------------------------------------------------------
|
||||
import json as _json
|
||||
|
||||
@router.get('/profile/world-config')
|
||||
async def get_world_config(user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
row = conn.execute("SELECT world_config FROM users WHERE id=?", (user['id'],)).fetchone()
|
||||
cfg = row['world_config'] if row and row['world_config'] else None
|
||||
return {"config": _json.loads(cfg) if cfg else None}
|
||||
|
||||
|
||||
class WorldConfigIn(BaseModel):
|
||||
config: dict
|
||||
|
||||
@router.put('/profile/world-config')
|
||||
async def put_world_config(body: WorldConfigIn, user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
conn.execute("UPDATE users SET world_config=? WHERE id=?",
|
||||
(_json.dumps(body.config), user['id']))
|
||||
return {"status": "ok"}
|
||||
|
|
|
|||
|
|
@ -6954,7 +6954,7 @@ svg.empty-state-icon {
|
|||
/* FAB */
|
||||
.exp-fab {
|
||||
position: fixed;
|
||||
bottom: calc(var(--nav-height, 64px) + var(--space-4));
|
||||
bottom: calc(var(--nav-bottom-height) + var(--safe-bottom) + var(--space-2));
|
||||
right: var(--space-4);
|
||||
z-index: 100;
|
||||
width: 52px;
|
||||
|
|
|
|||
|
|
@ -93,9 +93,9 @@
|
|||
</script>
|
||||
|
||||
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=652">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=652">
|
||||
<link rel="stylesheet" href="/css/components.css?v=652">
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=653">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=653">
|
||||
<link rel="stylesheet" href="/css/components.css?v=653">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
@ -539,9 +539,6 @@
|
|||
<span class="wlabel" data-w="1">HUND</span>
|
||||
<span class="wlabel" data-w="2">WELT</span>
|
||||
</div>
|
||||
<button id="worlds-settings" aria-label="Einstellungen">
|
||||
<svg class="ph-icon" style="width:20px;height:20px"><use href="/icons/phosphor.svg#gear"></use></svg>
|
||||
</button>
|
||||
<div id="worlds-track">
|
||||
<div class="world-panel" id="wp-jetzt"><div id="wj-content"></div></div>
|
||||
<div class="world-panel" id="wp-hund"><div id="wh-content"></div></div>
|
||||
|
|
@ -565,7 +562,7 @@
|
|||
<script src="/js/api.js?v=94"></script>
|
||||
<script src="/js/ui.js?v=94"></script>
|
||||
<script src="/js/app.js?v=94"></script>
|
||||
<script src="/js/worlds.js?v=652"></script>
|
||||
<script src="/js/worlds.js?v=653"></script>
|
||||
|
||||
<!-- Feature-Seiten werden lazy geladen -->
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '652'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '653'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VERSION = '1.3.0'; // ← semantische Version, wird bei make release gesetzt
|
||||
const IS_STAGING = location.hostname === 'staging.banyaro.app';
|
||||
|
||||
|
|
|
|||
|
|
@ -839,14 +839,18 @@ window.Page_map = (() => {
|
|||
}
|
||||
|
||||
const PIN_TYPES = [
|
||||
{ type: 'giftkoeder', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#skull"></use></svg>', label: 'Giftköder', color: '#DC2626' }, // ← wichtigster Typ, immer oben
|
||||
{ type: 'waste_basket', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trash"></use></svg>', label: 'Mülleimer', color: '#6B7280' },
|
||||
{ type: 'kotbeutel', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#bag"></use></svg>', label: 'Kotbeutel', color: '#84A98C' },
|
||||
{ type: 'drinking_water', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#drop"></use></svg>', label: 'Wasserstelle', color: '#0EA5E9' },
|
||||
{ type: 'dog_park', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#leaf"></use></svg>', label: 'Hundewiese', color: '#15803D' },
|
||||
{ type: 'parkplatz', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#car"></use></svg>', label: 'Parkplatz', color: '#2563EB' },
|
||||
{ type: 'treffpunkt', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#handshake"></use></svg>', label: 'Treffpunkt', color: '#7C3AED' },
|
||||
{ type: 'sonstiges', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#push-pin"></use></svg>', label: 'Sonstiges', color: '#F59E0B' },
|
||||
{ type: 'giftkoeder', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#skull"></use></svg>', label: 'Giftköder', color: '#DC2626' },
|
||||
{ type: 'waste_basket', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trash"></use></svg>', label: 'Mülleimer', color: '#6B7280' },
|
||||
{ type: 'kotbeutel', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#bag"></use></svg>', label: 'Kotbeutel', color: '#84A98C' },
|
||||
{ type: 'kotbeutel_abfall', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#bag"></use></svg>', label: 'Kotbeutel + Mülleimer', color: '#5a8a6a' },
|
||||
{ type: 'bank', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#park"></use></svg>', label: 'Sitzbank', color: '#92400E' },
|
||||
{ type: 'bank_kotbeutel', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#park"></use></svg>', label: 'Bank + Kotbeutel', color: '#7a6030' },
|
||||
{ type: 'bank_kotbeutel_abfall', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#park"></use></svg>', label: 'Bank + Kotbeutel + Mülleimer', color: '#4a5a2a' },
|
||||
{ type: 'drinking_water', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#drop"></use></svg>', label: 'Wasserstelle', color: '#0EA5E9' },
|
||||
{ type: 'dog_park', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#leaf"></use></svg>', label: 'Hundewiese', color: '#15803D' },
|
||||
{ type: 'parkplatz', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#car"></use></svg>', label: 'Parkplatz', color: '#2563EB' },
|
||||
{ type: 'treffpunkt', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#handshake"></use></svg>', label: 'Treffpunkt', color: '#7C3AED' },
|
||||
{ type: 'sonstiges', icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#push-pin"></use></svg>', label: 'Sonstiges', color: '#F59E0B' },
|
||||
];
|
||||
|
||||
function _confirmPlacement(latlng) {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ window.Worlds = (() => {
|
|||
_setupButtons();
|
||||
_goTo(_cur, false);
|
||||
show();
|
||||
// Config aus DB laden (async, dann neu rendern wenn nötig)
|
||||
await _loadConfigFromServer();
|
||||
// Welten parallel rendern
|
||||
_renderJetzt();
|
||||
_renderHund();
|
||||
|
|
@ -159,7 +161,6 @@ window.Worlds = (() => {
|
|||
|
||||
function _setupButtons() {
|
||||
document.getElementById('worlds-fab')?.addEventListener('click', _openFab);
|
||||
document.getElementById('worlds-settings')?.addEventListener('click', () => navigateTo('settings'));
|
||||
document.getElementById('worlds-back')?.addEventListener('click', () => show());
|
||||
document.querySelectorAll('.wdot').forEach((dot, i) => {
|
||||
dot.style.pointerEvents = 'auto';
|
||||
|
|
@ -296,12 +297,33 @@ window.Worlds = (() => {
|
|||
welt: ['map','forum','friends','walks','poison','recalls','lost','routes','events','jobs','knigge','movies'],
|
||||
};
|
||||
|
||||
function _getConfig() {
|
||||
try { return JSON.parse(localStorage.getItem('world_chips') || 'null') || _DEFAULT_CONFIG; }
|
||||
catch { return _DEFAULT_CONFIG; }
|
||||
// _cfgCache: wird beim Init aus DB geladen, Fallback localStorage → Default
|
||||
let _cfgCache = null;
|
||||
|
||||
async function _loadConfigFromServer() {
|
||||
try {
|
||||
const res = await API.get('/profile/world-config');
|
||||
if (res?.config) {
|
||||
_cfgCache = res.config;
|
||||
try { localStorage.setItem('world_chips', JSON.stringify(_cfgCache)); } catch {}
|
||||
return;
|
||||
}
|
||||
} catch {}
|
||||
// Fallback: localStorage
|
||||
try { _cfgCache = JSON.parse(localStorage.getItem('world_chips') || 'null') || _DEFAULT_CONFIG; }
|
||||
catch { _cfgCache = _DEFAULT_CONFIG; }
|
||||
}
|
||||
|
||||
function _getConfig() {
|
||||
return _cfgCache || _DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
function _saveConfig(cfg) {
|
||||
_cfgCache = cfg;
|
||||
try { localStorage.setItem('world_chips', JSON.stringify(cfg)); } catch {}
|
||||
if (_state?.user) {
|
||||
API.put('/profile/world-config', { config: cfg }).catch(() => {});
|
||||
}
|
||||
}
|
||||
function _chipMeta(page) {
|
||||
return _ALL_CHIPS.find(c => c.page === page) || null;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Offline-Cache + Push Notifications + Tile-Cache
|
||||
============================================================ */
|
||||
|
||||
const CACHE_VERSION = 'by-v652';
|
||||
const CACHE_VERSION = 'by-v653';
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue