v2.5.2 — PWA, new branding, bug fixes

Progressive Web App:
- Service worker with Workbox caching (map tiles, API, uploads, CDN)
- Web app manifest with standalone display mode
- Custom app icon with PNG generation from SVG
- Apple meta tags, dynamic theme-color for dark/light mode
- iOS safe area handling

New Branding:
- Custom NOMAD logo (icon + text variants for light/dark mode)
- Logo used in navbar, login page, demo banner, admin, PDF export
- MuseoModerno font for login tagline
- Plane takeoff animation on login
- Liquid glass hover effect on dashboard spotlight & widgets
- Brand images protected from save/copy/drag
- "made with NOMAD" footer on PDF exports

Bug Fixes:
- Fix mobile note reorder (missing tripId prop)
- Fix Atlas city counting (strip postal codes, normalize case)
- Fix Atlas country detection (add Japanese/Korean/Thai names)
- Fix PDF note positioning (use order_index instead of sort_order)
- Fix PDF note icons (render actual icon instead of hardcoded notepad)
- Fix file source badge overflow on mobile (text truncation)
- Fix navbar dropdown z-index overlap with mobile plan/places buttons
- Fix dashboard trip card hover contrast in dark mode
- Fix day header hover color matching place background in dark mode
- Shorten settings button labels on mobile

UI Improvements:
- Mobile navbar shows icon only, desktop shows full logo
- NOMAD version badge in profile dropdown
- Top padding before first item in day planner
- Improved drag & drop stability (larger drop zones, less flickering)
This commit is contained in:
Maurice
2026-03-22 02:50:13 +01:00
parent 5f4e7f9487
commit dd3d4263a7
22 changed files with 369 additions and 100 deletions
+10 -10
View File
@@ -4,6 +4,8 @@ import { useTranslation } from '../../i18n'
const texts = {
de: {
titleBefore: 'Willkommen bei ',
titleAfter: '',
title: 'Willkommen zur NOMAD Demo',
description: 'Du kannst Reisen ansehen, bearbeiten und eigene erstellen. Alle Aenderungen werden jede Stunde automatisch zurueckgesetzt.',
resetIn: 'Naechster Reset in',
@@ -34,6 +36,8 @@ const texts = {
close: 'Verstanden',
},
en: {
titleBefore: 'Welcome to ',
titleAfter: '',
title: 'Welcome to the NOMAD Demo',
description: 'You can view, edit and create trips. All changes are automatically reset every hour.',
resetIn: 'Next reset in',
@@ -98,15 +102,9 @@ export default function DemoBanner() {
{/* Header */}
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
<div style={{
width: 36, height: 36, borderRadius: 10,
background: '#111827',
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<Plane size={18} style={{ color: 'white' }} />
</div>
<h2 style={{ margin: 0, fontSize: 17, fontWeight: 700, color: '#111827' }}>
{t.title}
<img src="/icons/icon-dark.svg" alt="" style={{ width: 36, height: 36, borderRadius: 10 }} />
<h2 style={{ margin: 0, fontSize: 17, fontWeight: 700, color: '#111827', display: 'flex', alignItems: 'center', gap: 5 }}>
{t.titleBefore}<img src="/text-dark.svg" alt="NOMAD" style={{ height: 18 }} />{t.titleAfter}
</h2>
</div>
@@ -141,7 +139,9 @@ export default function DemoBanner() {
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
<Map size={14} style={{ color: '#111827' }} />
<span style={{ fontSize: 12, fontWeight: 700, color: '#111827' }}>{t.whatIs}</span>
<span style={{ fontSize: 12, fontWeight: 700, color: '#111827', display: 'flex', alignItems: 'center', gap: 4 }}>
{language === 'de' ? 'Was ist ' : 'What is '}<img src="/text-dark.svg" alt="NOMAD" style={{ height: 13, marginRight: -2 }} />?
</span>
</div>
<p style={{ fontSize: 12, color: '#64748b', lineHeight: 1.5, margin: 0 }}>{t.whatIsDesc}</p>
</div>
+15 -12
View File
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react'
import React, { useState, useEffect, useRef } from 'react'
import ReactDOM from 'react-dom'
import { Link, useNavigate, useLocation } from 'react-router-dom'
import { useAuthStore } from '../../store/authStore'
import { useSettingsStore } from '../../store/settingsStore'
@@ -72,10 +73,9 @@ export default function Navbar({ tripTitle, tripId, onBack, showBack, onShare })
</button>
)}
<Link to="/dashboard" className="flex items-center gap-2 transition-colors flex-shrink-0"
style={{ color: 'var(--text-primary)' }}>
<Plane className="w-5 h-5" style={{ color: 'var(--text-primary)' }} />
<span className="font-bold text-sm hidden sm:inline">NOMAD</span>
<Link to="/dashboard" className="flex items-center transition-colors flex-shrink-0">
<img src={dark ? '/icons/icon-white.svg' : '/icons/icon-dark.svg'} alt="NOMAD" className="sm:hidden" style={{ height: 22, width: 22 }} />
<img src={dark ? '/logo-light.svg' : '/logo-dark.svg'} alt="NOMAD" className="hidden sm:block" style={{ height: 28 }} />
</Link>
{/* Global addon nav items */}
@@ -169,11 +169,10 @@ export default function Navbar({ tripTitle, tripId, onBack, showBack, onShare })
<ChevronDown className="w-4 h-4" style={{ color: 'var(--text-faint)' }} />
</button>
{userMenuOpen && (
{userMenuOpen && ReactDOM.createPortal(
<>
<div className="fixed inset-0 z-10" onClick={() => setUserMenuOpen(false)} />
<div className="absolute right-0 top-full mt-2 w-52 rounded-xl shadow-xl border z-20 overflow-hidden"
style={{ background: 'var(--bg-card)', borderColor: 'var(--border-primary)' }}>
<div style={{ position: 'fixed', inset: 0, zIndex: 9998 }} onClick={() => setUserMenuOpen(false)} />
<div className="w-52 rounded-xl shadow-xl border overflow-hidden" style={{ position: 'fixed', top: 'var(--nav-h)', right: 8, zIndex: 9999, background: 'var(--bg-card)', borderColor: 'var(--border-primary)' }}>
<div className="px-4 py-3 border-b" style={{ borderColor: 'var(--border-secondary)' }}>
<p className="text-sm font-medium" style={{ color: 'var(--text-primary)' }}>{user.username}</p>
<p className="text-xs truncate" style={{ color: 'var(--text-muted)' }}>{user.email}</p>
@@ -213,13 +212,17 @@ export default function Navbar({ tripTitle, tripId, onBack, showBack, onShare })
{t('nav.logout')}
</button>
{appVersion && (
<div className="px-4 py-1.5 text-center" style={{ fontSize: 10, color: 'var(--text-faint)' }}>
NOMAD v{appVersion}
<div className="px-4 pt-2 pb-2.5 text-center" style={{ marginTop: 4, borderTop: '1px solid var(--border-secondary)' }}>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 5, background: 'var(--bg-tertiary)', borderRadius: 99, padding: '4px 12px' }}>
<img src={dark ? '/text-light.svg' : '/text-dark.svg'} alt="NOMAD" style={{ height: 10, opacity: 0.5 }} />
<span style={{ fontSize: 10, fontWeight: 600, color: 'var(--text-faint)' }}>v{appVersion}</span>
</div>
</div>
)}
</div>
</div>
</>
</>,
document.body
)}
</div>
)}