Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ed9ac6941 | |||
| b4fff04ee1 | |||
| 7e01106801 | |||
| caf6e395cd | |||
| a67575f4d2 | |||
| c2d620025e | |||
| 1524321afd |
+326
-1
@@ -63,6 +63,16 @@ body {
|
||||
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 {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
@@ -71,7 +81,7 @@ body {
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin: 0 0 14px 0;
|
||||
margin: 0;
|
||||
line-height: 1.25;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
@@ -895,6 +905,36 @@ html.scheme-dark .themed-select-option.is-selected {
|
||||
color: var(--app-text-heading);
|
||||
}
|
||||
|
||||
.section-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.copy-link-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.copy-link-row .input-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.form-actions--start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.btn-refresh {
|
||||
background: none;
|
||||
border: none;
|
||||
@@ -1635,6 +1675,224 @@ html.scheme-dark .themed-select-option.is-selected {
|
||||
.hide-mobile {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.dashboard-header,
|
||||
.app-header {
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.app-header-left {
|
||||
flex: 1 1 100%;
|
||||
min-width: 0;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.app-title-area {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.app-title-area h2 {
|
||||
font-size: 17px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.app-title-row {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.app-subtitle {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.header-brand {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.header-brand h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
flex: 1 1 100%;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.conn-status > span:not(.pulse-dot) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.skipper-badge__name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn-back {
|
||||
padding: 8px 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.section-title-bar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.section-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-toolbar .btn {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.section-toolbar .btn.primary {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.section-title-left {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-title-left .form-header h2 {
|
||||
font-size: 16px;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.logbooks-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.logbook-card {
|
||||
flex-wrap: wrap;
|
||||
padding: 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card-meta {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.card-info h3 {
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.crew-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.copy-link-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.copy-link-row .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-actions--start {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.form-actions--start .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.table-responsive table {
|
||||
min-width: 480px;
|
||||
}
|
||||
|
||||
.custom-dialog-overlay {
|
||||
padding: max(16px, env(safe-area-inset-left)) max(16px, env(safe-area-inset-right));
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.custom-dialog-card {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
padding: 22px 18px;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0px);
|
||||
}
|
||||
|
||||
.custom-dialog-actions {
|
||||
flex-direction: column-reverse;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.custom-dialog-actions .btn {
|
||||
width: 100%;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.disclaimer-modal-overlay {
|
||||
padding: max(12px, env(safe-area-inset-left)) max(12px, env(safe-area-inset-right));
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.disclaimer-modal-panel,
|
||||
.registration-disclaimer--modal {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
padding: 28px 20px;
|
||||
max-width: calc(100vw - 24px);
|
||||
}
|
||||
|
||||
.app-layout,
|
||||
.dashboard-container {
|
||||
padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
|
||||
.track-info-left {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.track-actions {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.track-actions .btn {
|
||||
flex: 1 1 calc(50% - 4px);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#openseamap-container,
|
||||
.track-map-container {
|
||||
height: min(360px, 45svh);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================== */
|
||||
@@ -2257,6 +2515,12 @@ html.theme-cupertino .events-scroll-container {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.track-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.track-error-msg {
|
||||
color: #ef4444;
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
@@ -3191,6 +3455,28 @@ html.theme-cupertino .events-scroll-container {
|
||||
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 {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -3320,7 +3606,9 @@ body.app-tour-active .app-tour-target-active {
|
||||
.app-tour-tooltip {
|
||||
position: fixed;
|
||||
z-index: 10002;
|
||||
box-sizing: border-box;
|
||||
width: min(420px, calc(100vw - 32px));
|
||||
max-width: calc(100vw - 32px);
|
||||
padding: 20px 20px 16px;
|
||||
border-radius: 16px;
|
||||
background: #1e293b;
|
||||
@@ -3329,10 +3617,19 @@ body.app-tour-active .app-tour-target-active {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.app-tour-tooltip:not(.centered) {
|
||||
left: max(16px, env(safe-area-inset-left, 0px));
|
||||
right: max(16px, env(safe-area-inset-right, 0px));
|
||||
width: auto;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.app-tour-tooltip.centered {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: min(420px, calc(100vw - 32px - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)));
|
||||
max-width: calc(100vw - 32px - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px));
|
||||
}
|
||||
|
||||
.app-tour-close {
|
||||
@@ -3409,6 +3706,34 @@ body.app-tour-active .app-tour-target-active {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.app-tour-tooltip {
|
||||
padding: 18px 16px 14px;
|
||||
}
|
||||
|
||||
.app-tour-actions {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.app-tour-nav {
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app-tour-nav-btn {
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
body.app-tour-active .pwa-install-banner {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.app-tour-active .disclaimer-modal-overlay.feedback-modal-overlay--tour {
|
||||
|
||||
+5
-3
@@ -28,6 +28,7 @@ import PwaInstallPrompt from './components/PwaInstallPrompt.tsx'
|
||||
import PwaUpdatePrompt from './components/PwaUpdatePrompt.tsx'
|
||||
import AppFooter from './components/AppFooter.tsx'
|
||||
import LogbookRoleBadge from './components/LogbookRoleBadge.tsx'
|
||||
import BetaBadge from './components/BetaBadge.tsx'
|
||||
import { db } from './services/db.js'
|
||||
import { getLogbookAccess } from './services/logbookAccess.js'
|
||||
import type { LogbookAccessRole } from './services/logbook.js'
|
||||
@@ -420,7 +421,7 @@ function App() {
|
||||
)
|
||||
}
|
||||
|
||||
const pwaInstallBanner = <PwaInstallPrompt variant="banner" />
|
||||
const pwaInstallBanner = !isActive ? <PwaInstallPrompt variant="banner" /> : null
|
||||
|
||||
const logbookReadOnly =
|
||||
activeLogbookRecord?.isShared === 1 && activeAccessRole === 'READ'
|
||||
@@ -445,13 +446,14 @@ function App() {
|
||||
{/* Active Logbook Header */}
|
||||
<header className="app-header">
|
||||
<div className="app-header-left">
|
||||
<button className="btn-back" onClick={handleBackToDashboard}>
|
||||
<button className="btn-back" onClick={handleBackToDashboard} title={t('nav.dashboard')}>
|
||||
<ChevronLeft size={16} />
|
||||
{t('nav.dashboard')}
|
||||
<span className="hide-mobile">{t('nav.dashboard')}</span>
|
||||
</button>
|
||||
<div className="app-title-area">
|
||||
<div className="app-title-row">
|
||||
<h2>{activeLogbookTitle}</h2>
|
||||
<BetaBadge />
|
||||
{activeAccessRole && activeAccessRole !== 'OWNER' && (
|
||||
<LogbookRoleBadge role={activeAccessRole} />
|
||||
)}
|
||||
|
||||
@@ -15,12 +15,33 @@ interface SpotlightRect {
|
||||
height: number
|
||||
}
|
||||
|
||||
const TOOLTIP_EDGE_MARGIN = 16
|
||||
const TOOLTIP_ESTIMATED_HEIGHT = 240
|
||||
|
||||
function buildCutoutClipPath(rect: SpotlightRect): string {
|
||||
const right = rect.left + rect.width
|
||||
const bottom = rect.top + rect.height
|
||||
return `polygon(evenodd, 0 0, 100vw 0, 100vw 100vh, 0 100vh, 0 0, ${rect.left}px ${rect.top}px, ${right}px ${rect.top}px, ${right}px ${bottom}px, ${rect.left}px ${bottom}px, ${rect.left}px ${rect.top}px)`
|
||||
}
|
||||
|
||||
function computeTooltipTop(spotlight: SpotlightRect): number {
|
||||
const viewportBottom = window.innerHeight - TOOLTIP_EDGE_MARGIN
|
||||
const below = spotlight.top + spotlight.height + 12
|
||||
if (below + TOOLTIP_ESTIMATED_HEIGHT <= viewportBottom) {
|
||||
return below
|
||||
}
|
||||
|
||||
const above = spotlight.top - 12 - TOOLTIP_ESTIMATED_HEIGHT
|
||||
if (above >= TOOLTIP_EDGE_MARGIN) {
|
||||
return above
|
||||
}
|
||||
|
||||
return Math.max(
|
||||
TOOLTIP_EDGE_MARGIN,
|
||||
Math.min(below, viewportBottom - TOOLTIP_ESTIMATED_HEIGHT)
|
||||
)
|
||||
}
|
||||
|
||||
export default function AppTourOverlay() {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
@@ -111,12 +132,8 @@ export default function AppTourOverlay() {
|
||||
const tooltipStyle = centered
|
||||
? undefined
|
||||
: spotlight
|
||||
? {
|
||||
top: Math.min(window.innerHeight - 220, spotlight.top + spotlight.height + 12),
|
||||
left: Math.min(window.innerWidth - 340, Math.max(16, spotlight.left)),
|
||||
maxWidth: '420px'
|
||||
}
|
||||
: { top: '20%', left: '50%', transform: 'translateX(-50%)', maxWidth: '420px' }
|
||||
? { top: computeTooltipTop(spotlight) }
|
||||
: { top: '20%' }
|
||||
|
||||
const backdropStyle = spotlight && !centered
|
||||
? { clipPath: buildCutoutClipPath(spotlight) }
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from '../services/auth.js'
|
||||
import { KeyRound, ShieldAlert, Languages, HelpCircle, UserRound, X } from 'lucide-react'
|
||||
import RegistrationDisclaimer from './RegistrationDisclaimer.tsx'
|
||||
import BetaBadge from './BetaBadge.tsx'
|
||||
|
||||
interface AuthOnboardingProps {
|
||||
onAuthenticated: () => void
|
||||
@@ -408,7 +409,10 @@ export default function AuthOnboarding({ onAuthenticated, onOpenDemo }: AuthOnbo
|
||||
<div className="auth-card glass">
|
||||
<div className="auth-brand">
|
||||
<img src="/logo.png" alt="Kapteins Daagbok" className="auth-logo-img" />
|
||||
<h1>{t('app.name')}</h1>
|
||||
<div className="auth-brand-title-row">
|
||||
<h1>{t('app.name')}</h1>
|
||||
<BetaBadge />
|
||||
</div>
|
||||
<p className="tagline">{t('auth.tagline')}</p>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
@@ -372,7 +372,7 @@ export default function LogEntriesList({
|
||||
<Calendar size={24} className="form-icon" />
|
||||
<h2>{t('logs.title')}</h2>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||
<div className="section-toolbar">
|
||||
<button className="btn secondary" onClick={handleDownloadCsv} disabled={loading || exporting || entries.length === 0} style={{ width: 'auto', padding: '8px 16px' }} title={t('logs.export_csv')}>
|
||||
<Download size={16} />
|
||||
<span className="hide-mobile">{exporting ? t('logs.exporting') : t('logs.export_csv')}</span>
|
||||
@@ -384,9 +384,9 @@ export default function LogEntriesList({
|
||||
</button>
|
||||
|
||||
{!readOnly && (
|
||||
<button className="btn primary" onClick={handleCreate} disabled={loading || exporting} style={{ width: 'auto', padding: '8px 16px' }}>
|
||||
<button className="btn primary" onClick={handleCreate} disabled={loading || exporting} style={{ width: 'auto', padding: '8px 16px' }} title={t('logs.new_entry')}>
|
||||
<Plus size={16} />
|
||||
{t('logs.new_entry')}
|
||||
<span className="hide-mobile">{t('logs.new_entry')}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -992,7 +992,7 @@ export default function LogEntryEditor({
|
||||
style={{ width: 'auto', padding: '8px 16px' }}
|
||||
>
|
||||
<Download size={16} />
|
||||
<span>{exporting ? t('logs.exporting_pdf') : t('logs.export_pdf')}</span>
|
||||
<span className="hide-mobile">{exporting ? t('logs.exporting_pdf') : t('logs.export_pdf')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1567,15 +1567,16 @@ export default function LogEntryEditor({
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
||||
<div className="track-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary"
|
||||
onClick={() => downloadTrackFile(savedTrack)}
|
||||
style={{ width: 'auto', padding: '6px 12px', fontSize: '13px', display: 'flex', alignItems: 'center', gap: '4px' }}
|
||||
title={t('logs.gps_tracking_btn_gpx')}
|
||||
>
|
||||
<Download size={14} />
|
||||
{t('logs.gps_tracking_btn_gpx')}
|
||||
<span className="hide-mobile">{t('logs.gps_tracking_btn_gpx')}</span>
|
||||
</button>
|
||||
{!readOnly && (
|
||||
<button
|
||||
@@ -1583,9 +1584,10 @@ export default function LogEntryEditor({
|
||||
className="btn secondary"
|
||||
onClick={handleDeleteTrack}
|
||||
style={{ width: 'auto', padding: '6px 12px', fontSize: '13px', display: 'flex', alignItems: 'center', gap: '4px', background: 'rgba(239, 68, 68, 0.1)', color: '#ef4444', borderColor: 'rgba(239, 68, 68, 0.2)' }}
|
||||
title={t('logs.gps_track_delete')}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
{t('logs.gps_track_delete')}
|
||||
<span className="hide-mobile">{t('logs.gps_track_delete')}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useLiveQuery } from 'dexie-react-hooks'
|
||||
import { db } from '../services/db.js'
|
||||
import { fetchLogbooks, createLogbook, deleteLogbook, type DecryptedLogbook } from '../services/logbook.js'
|
||||
import LogbookRoleBadge from './LogbookRoleBadge.tsx'
|
||||
import BetaBadge from './BetaBadge.tsx'
|
||||
import { PlausibleEvents, trackPlausibleEvent } from '../services/analytics.js'
|
||||
import { logoutUser } from '../services/auth.js'
|
||||
import { useDialog } from './ModalDialog.tsx'
|
||||
@@ -177,7 +178,10 @@ export default function LogbookDashboard({ onSelectLogbook, onLogout }: LogbookD
|
||||
<div className="header-brand">
|
||||
<Ship className="header-logo" size={32} />
|
||||
<div>
|
||||
<h1>{t('app.name')}</h1>
|
||||
<div className="header-brand-title-row">
|
||||
<h1>{t('app.name')}</h1>
|
||||
<BetaBadge />
|
||||
</div>
|
||||
<p className="subtitle">{t('app.tagline')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -414,7 +414,7 @@ export default function SettingsForm({ logbookId, onLogbookRestored }: SettingsF
|
||||
</div>
|
||||
|
||||
{shareEnabled && shareLink && (
|
||||
<div className="input-group mb-4" style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||
<div className="input-group mb-4 copy-link-row">
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
@@ -455,7 +455,7 @@ export default function SettingsForm({ logbookId, onLogbookRestored }: SettingsF
|
||||
{t('logs.invite_link_desc')}
|
||||
</p>
|
||||
|
||||
<div className="form-actions" style={{ justifyContent: 'flex-start', gap: '12px', marginBottom: '20px' }}>
|
||||
<div className="form-actions form-actions--start" style={{ gap: '12px', marginBottom: '20px' }}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn primary"
|
||||
@@ -469,7 +469,7 @@ export default function SettingsForm({ logbookId, onLogbookRestored }: SettingsF
|
||||
</div>
|
||||
|
||||
{inviteLink && (
|
||||
<div className="input-group mb-6" style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||
<div className="input-group mb-6 copy-link-row">
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
"translation": {
|
||||
"app": {
|
||||
"name": "Kapteins Daagbok",
|
||||
"tagline": "Privates Yacht-Logbuch"
|
||||
"tagline": "Privates Yacht-Logbuch",
|
||||
"beta": "Beta",
|
||||
"beta_hint": "Beta-Version — Funktionen können sich noch ändern"
|
||||
},
|
||||
"nav": {
|
||||
"dashboard": "Dashboard",
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
"translation": {
|
||||
"app": {
|
||||
"name": "Kapteins Daagbok",
|
||||
"tagline": "Private Yacht Logbook"
|
||||
"tagline": "Private Yacht Logbook",
|
||||
"beta": "Beta",
|
||||
"beta_hint": "Beta release — features may still change"
|
||||
},
|
||||
"nav": {
|
||||
"dashboard": "Dashboard",
|
||||
|
||||
@@ -250,9 +250,12 @@
|
||||
<section class="features" aria-label="Funktionen">
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Reisetage im nautischen Logbuch-Format (Hafen, Wetter, Besegelung, Crew, Tankstände, etc.)</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Offline-fähige PWA — läuft auf jedem Smartphone & Tablet</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Passkey-Anmeldung & Ende-zu-Ende Verschlüsselung</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>GPS-Track Upload (GPX/KML), Karte & Streckenstatistik</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Einfache passwortlose Passkey-Anmeldung</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Ende-zu-Ende Verschlüsselung</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>GPS-Track Upload (GPX/KML) mit Kartendarstellung</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Streckenstatistik</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Foto-Anhänge pro Reisetag</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Foto-Anhänge für Skipper und Crew</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Crew einladen — gemeinsam am Logbuch arbeiten</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>PDF- & CSV-Export</span></div>
|
||||
<div class="feature"><span class="feature-icon">✦</span><span>Verschlüsseltes Backup & Wiederherstellung</span></div>
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user