feat(ui): Beta-Badge in Login-, Dashboard- und Logbuch-Titelzeile
Wiederverwendbare BetaBadge-Komponente mit i18n-Tooltip. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+33
-1
@@ -63,6 +63,16 @@ body {
|
|||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auth-brand-title-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-brand-title-row h1,
|
||||||
.auth-brand h1 {
|
.auth-brand h1 {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -71,7 +81,7 @@ body {
|
|||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
margin: 0 0 14px 0;
|
margin: 0;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
letter-spacing: -0.5px;
|
letter-spacing: -0.5px;
|
||||||
}
|
}
|
||||||
@@ -3191,6 +3201,28 @@ html.theme-cupertino .events-scroll-container {
|
|||||||
border: 1px solid rgba(251, 191, 36, 0.25);
|
border: 1px solid rgba(251, 191, 36, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.beta-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--app-accent-light);
|
||||||
|
background: var(--app-accent-bg);
|
||||||
|
border: 1px solid var(--app-accent-focus-ring);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-brand-title-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.role-badge {
|
.role-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import PwaInstallPrompt from './components/PwaInstallPrompt.tsx'
|
|||||||
import PwaUpdatePrompt from './components/PwaUpdatePrompt.tsx'
|
import PwaUpdatePrompt from './components/PwaUpdatePrompt.tsx'
|
||||||
import AppFooter from './components/AppFooter.tsx'
|
import AppFooter from './components/AppFooter.tsx'
|
||||||
import LogbookRoleBadge from './components/LogbookRoleBadge.tsx'
|
import LogbookRoleBadge from './components/LogbookRoleBadge.tsx'
|
||||||
|
import BetaBadge from './components/BetaBadge.tsx'
|
||||||
import { db } from './services/db.js'
|
import { db } from './services/db.js'
|
||||||
import { getLogbookAccess } from './services/logbookAccess.js'
|
import { getLogbookAccess } from './services/logbookAccess.js'
|
||||||
import type { LogbookAccessRole } from './services/logbook.js'
|
import type { LogbookAccessRole } from './services/logbook.js'
|
||||||
@@ -452,6 +453,7 @@ function App() {
|
|||||||
<div className="app-title-area">
|
<div className="app-title-area">
|
||||||
<div className="app-title-row">
|
<div className="app-title-row">
|
||||||
<h2>{activeLogbookTitle}</h2>
|
<h2>{activeLogbookTitle}</h2>
|
||||||
|
<BetaBadge />
|
||||||
{activeAccessRole && activeAccessRole !== 'OWNER' && (
|
{activeAccessRole && activeAccessRole !== 'OWNER' && (
|
||||||
<LogbookRoleBadge role={activeAccessRole} />
|
<LogbookRoleBadge role={activeAccessRole} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from '../services/auth.js'
|
} from '../services/auth.js'
|
||||||
import { KeyRound, ShieldAlert, Languages, HelpCircle, UserRound, X } from 'lucide-react'
|
import { KeyRound, ShieldAlert, Languages, HelpCircle, UserRound, X } from 'lucide-react'
|
||||||
import RegistrationDisclaimer from './RegistrationDisclaimer.tsx'
|
import RegistrationDisclaimer from './RegistrationDisclaimer.tsx'
|
||||||
|
import BetaBadge from './BetaBadge.tsx'
|
||||||
|
|
||||||
interface AuthOnboardingProps {
|
interface AuthOnboardingProps {
|
||||||
onAuthenticated: () => void
|
onAuthenticated: () => void
|
||||||
@@ -408,7 +409,10 @@ export default function AuthOnboarding({ onAuthenticated, onOpenDemo }: AuthOnbo
|
|||||||
<div className="auth-card glass">
|
<div className="auth-card glass">
|
||||||
<div className="auth-brand">
|
<div className="auth-brand">
|
||||||
<img src="/logo.png" alt="Kapteins Daagbok" className="auth-logo-img" />
|
<img src="/logo.png" alt="Kapteins Daagbok" className="auth-logo-img" />
|
||||||
|
<div className="auth-brand-title-row">
|
||||||
<h1>{t('app.name')}</h1>
|
<h1>{t('app.name')}</h1>
|
||||||
|
<BetaBadge />
|
||||||
|
</div>
|
||||||
<p className="tagline">{t('auth.tagline')}</p>
|
<p className="tagline">{t('auth.tagline')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
interface BetaBadgeProps {
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BetaBadge({ className = '' }: BetaBadgeProps) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`beta-badge ${className}`.trim()}
|
||||||
|
title={t('app.beta_hint')}
|
||||||
|
aria-label={t('app.beta_hint')}
|
||||||
|
>
|
||||||
|
{t('app.beta')}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { useLiveQuery } from 'dexie-react-hooks'
|
|||||||
import { db } from '../services/db.js'
|
import { db } from '../services/db.js'
|
||||||
import { fetchLogbooks, createLogbook, deleteLogbook, type DecryptedLogbook } from '../services/logbook.js'
|
import { fetchLogbooks, createLogbook, deleteLogbook, type DecryptedLogbook } from '../services/logbook.js'
|
||||||
import LogbookRoleBadge from './LogbookRoleBadge.tsx'
|
import LogbookRoleBadge from './LogbookRoleBadge.tsx'
|
||||||
|
import BetaBadge from './BetaBadge.tsx'
|
||||||
import { PlausibleEvents, trackPlausibleEvent } from '../services/analytics.js'
|
import { PlausibleEvents, trackPlausibleEvent } from '../services/analytics.js'
|
||||||
import { logoutUser } from '../services/auth.js'
|
import { logoutUser } from '../services/auth.js'
|
||||||
import { useDialog } from './ModalDialog.tsx'
|
import { useDialog } from './ModalDialog.tsx'
|
||||||
@@ -177,7 +178,10 @@ export default function LogbookDashboard({ onSelectLogbook, onLogout }: LogbookD
|
|||||||
<div className="header-brand">
|
<div className="header-brand">
|
||||||
<Ship className="header-logo" size={32} />
|
<Ship className="header-logo" size={32} />
|
||||||
<div>
|
<div>
|
||||||
|
<div className="header-brand-title-row">
|
||||||
<h1>{t('app.name')}</h1>
|
<h1>{t('app.name')}</h1>
|
||||||
|
<BetaBadge />
|
||||||
|
</div>
|
||||||
<p className="subtitle">{t('app.tagline')}</p>
|
<p className="subtitle">{t('app.tagline')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
"translation": {
|
"translation": {
|
||||||
"app": {
|
"app": {
|
||||||
"name": "Kapteins Daagbok",
|
"name": "Kapteins Daagbok",
|
||||||
"tagline": "Privates Yacht-Logbuch"
|
"tagline": "Privates Yacht-Logbuch",
|
||||||
|
"beta": "Beta",
|
||||||
|
"beta_hint": "Beta-Version — Funktionen können sich noch ändern"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
"translation": {
|
"translation": {
|
||||||
"app": {
|
"app": {
|
||||||
"name": "Kapteins Daagbok",
|
"name": "Kapteins Daagbok",
|
||||||
"tagline": "Private Yacht Logbook"
|
"tagline": "Private Yacht Logbook",
|
||||||
|
"beta": "Beta",
|
||||||
|
"beta_hint": "Beta release — features may still change"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
|
|||||||
Reference in New Issue
Block a user