Fix demo banner: add EN/DE translations, fix navbar overlap

Banner is now in document flow instead of fixed position,
so it no longer covers the navigation. Language follows
the app's i18n setting.
This commit is contained in:
Maurice
2026-03-19 13:58:27 +01:00
parent c2bb9a904c
commit 4e33d710ea
2 changed files with 63 additions and 32 deletions

View File

@@ -90,7 +90,6 @@ export default function App() {
<TranslationProvider>
<ToastContainer />
{demoMode && isAuthenticated && <DemoBanner />}
<div style={demoMode && isAuthenticated ? { paddingTop: 36 } : undefined}>
<Routes>
<Route path="/" element={<RootRedirect />} />
<Route path="/login" element={<LoginPage />} />
@@ -137,7 +136,6 @@ export default function App() {
/>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</div>
</TranslationProvider>
)
}

View File

@@ -1,32 +1,65 @@
import React, { useState, useEffect } from 'react'
import { Info, X, Github, Shield, Key, Users, Database, ChevronDown, ChevronUp } from 'lucide-react'
import { Info, Github, Shield, Key, Users, Database, ChevronDown, ChevronUp } from 'lucide-react'
import { useTranslation } from '../../i18n'
const texts = {
de: {
title: 'Demo-Modus',
resetInfo: 'Aenderungen werden stuendlich zurueckgesetzt',
nextReset: 'naechster Reset in ~{min} Min.',
moreInfo: 'Mehr Info',
lessInfo: 'Weniger',
description: 'Du nutzt die NOMAD Demo. Du kannst Reisen ansehen, bearbeiten und eigene erstellen — alles wird jede Stunde automatisch zurueckgesetzt.',
fullVersionTitle: 'Diese Funktionen sind in der Vollversion verfuegbar:',
features: [
'API-Schluessel verwalten (Google Maps, Wetter)',
'Benutzer & Rechte verwalten',
'Automatische Backups & Wiederherstellung',
'Registrierung & Sicherheitseinstellungen',
],
selfHost: 'NOMAD ist Open Source — ',
selfHostLink: 'selbst hosten',
},
en: {
title: 'Demo Mode',
resetInfo: 'Changes are reset every hour',
nextReset: 'next reset in ~{min} min.',
moreInfo: 'More info',
lessInfo: 'Less',
description: 'You are using the NOMAD demo. You can view, edit and create trips — everything is automatically reset every hour.',
fullVersionTitle: 'These features are available in the full version:',
features: [
'API key management (Google Maps, Weather)',
'User & permission management',
'Automatic backups & restore',
'Registration & security settings',
],
selfHost: 'NOMAD is open source — ',
selfHostLink: 'self-host it',
},
}
const featureIcons = [Key, Users, Database, Shield]
export default function DemoBanner() {
const [expanded, setExpanded] = useState(false)
const [minutesLeft, setMinutesLeft] = useState(null)
const { language } = useTranslation()
const t = texts[language] || texts.en
useEffect(() => {
const update = () => {
const now = new Date()
setMinutesLeft(59 - now.getMinutes())
}
const update = () => setMinutesLeft(59 - new Date().getMinutes())
update()
const interval = setInterval(update, 30000)
return () => clearInterval(interval)
}, [])
const adminFeatures = [
{ icon: Key, text: 'API-Schluessel verwalten (Google Maps, Wetter)' },
{ icon: Users, text: 'Benutzer & Rechte verwalten' },
{ icon: Database, text: 'Automatische Backups & Wiederherstellung' },
{ icon: Shield, text: 'Registrierung & Sicherheitseinstellungen' },
]
const bannerHeight = expanded ? undefined : 36
return (
<div style={{ position: 'fixed', top: 0, left: 0, right: 0, zIndex: 300 }}>
<div style={{ background: 'linear-gradient(135deg, #f59e0b, #d97706)', zIndex: 300 }}>
{/* Main banner bar */}
<div style={{
background: 'linear-gradient(135deg, #f59e0b, #d97706)',
color: '#451a03',
padding: '8px 16px',
display: 'flex',
@@ -37,16 +70,15 @@ export default function DemoBanner() {
fontWeight: 600,
fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif",
minHeight: 36,
boxShadow: '0 2px 8px rgba(217, 119, 6, 0.3)',
}}>
<Info size={15} style={{ flexShrink: 0 }} />
<span>
Demo-Modus
{t.title}
<span style={{ fontWeight: 400, margin: '0 6px' }}>&middot;</span>
Aenderungen werden stuendlich zurueckgesetzt
{t.resetInfo}
{minutesLeft !== null && (
<span style={{ fontWeight: 400, opacity: 0.8, marginLeft: 4 }}>
(naechster Reset in ~{minutesLeft} Min.)
({t.nextReset.replace('{min}', minutesLeft)})
</span>
)}
</span>
@@ -69,7 +101,7 @@ export default function DemoBanner() {
}}
>
{expanded ? <ChevronUp size={13} /> : <ChevronDown size={13} />}
{expanded ? 'Weniger' : 'Mehr Info'}
{expanded ? t.lessInfo : t.moreInfo}
</button>
</div>
@@ -80,25 +112,26 @@ export default function DemoBanner() {
borderBottom: '1px solid #fbbf24',
padding: '16px 24px',
fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif",
boxShadow: '0 4px 12px rgba(0,0,0,0.08)',
}}>
<div style={{ maxWidth: 640, margin: '0 auto' }}>
<p style={{ fontSize: 13, color: '#92400e', margin: '0 0 12px', lineHeight: 1.6 }}>
Du nutzt die NOMAD Demo. Du kannst Reisen ansehen, bearbeiten und eigene erstellen
alles wird <strong>jede Stunde automatisch zurueckgesetzt</strong>.
{t.description}
</p>
<p style={{ fontSize: 12, fontWeight: 700, color: '#78350f', margin: '0 0 8px', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
Diese Funktionen sind in der Vollversion verfuegbar:
{t.fullVersionTitle}
</p>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: 6 }}>
{adminFeatures.map(({ icon: Icon, text }) => (
<div key={text} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5, color: '#92400e' }}>
<Icon size={14} style={{ flexShrink: 0, opacity: 0.7 }} />
<span>{text}</span>
</div>
))}
{t.features.map((text, i) => {
const Icon = featureIcons[i]
return (
<div key={text} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5, color: '#92400e' }}>
<Icon size={14} style={{ flexShrink: 0, opacity: 0.7 }} />
<span>{text}</span>
</div>
)
})}
</div>
<div style={{
@@ -112,14 +145,14 @@ export default function DemoBanner() {
color: '#92400e',
}}>
<Github size={14} />
<span>NOMAD ist Open Source </span>
<span>{t.selfHost}</span>
<a
href="https://github.com/mauriceboe/NOMAD"
target="_blank"
rel="noopener noreferrer"
style={{ color: '#78350f', fontWeight: 700, textDecoration: 'underline' }}
>
selbst hosten &rarr;
{t.selfHostLink} &rarr;
</a>
</div>
</div>