feat: add trial system UI with countdown banner
Some checks failed
Deploy Development / deploy (push) Failing after 24s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s

Component:
- TrialBanner.jsx: Displays remaining trial days with urgency levels

Features:
- Calculates days left from profile.trial_ends_at
- Three urgency levels:
  * Normal (>7 days): Accent blue, "Abo wählen"
  * Warning (≤7 days): Orange, "Abo wählen"
  * Urgent (≤3 days): Red + ⚠️, "Jetzt upgraden"
- Auto-hides when no trial or trial ended
- Responsive flex layout
- Call-to-action button links to /settings?tab=subscription

Integration:
- Added to Dashboard after header greeting
- Uses activeProfile from ProfileContext
- Clean, non-intrusive design

UX:
- Clear messaging: "Trial endet in X Tagen"
- Special case: "morgen" for 1 day left
- Color-coded severity (blue → orange → red)
- Prominent CTA button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-21 09:56:35 +01:00
parent 86f7a513fe
commit 961897ce2f
2 changed files with 90 additions and 0 deletions

View File

@ -0,0 +1,86 @@
import { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
export default function TrialBanner({ profile }) {
const [daysLeft, setDaysLeft] = useState(null)
useEffect(() => {
if (!profile?.trial_ends_at) {
setDaysLeft(null)
return
}
const trialEnd = new Date(profile.trial_ends_at)
const now = new Date()
const diff = trialEnd - now
const days = Math.ceil(diff / (1000 * 60 * 60 * 24))
setDaysLeft(days)
}, [profile])
// No trial or trial ended
if (daysLeft === null || daysLeft <= 0) return null
// Determine severity
const isUrgent = daysLeft <= 3
const isWarning = daysLeft <= 7
const bgColor = isUrgent ? '#FCEBEB' : isWarning ? '#FFF4E6' : 'var(--accent-light)'
const borderColor = isUrgent ? '#D85A30' : isWarning ? '#F59E0B' : 'var(--accent)'
const textColor = isUrgent ? '#D85A30' : isWarning ? '#D97706' : 'var(--accent-dark)'
return (
<div style={{
background: bgColor,
border: `2px solid ${borderColor}`,
borderRadius: 12,
padding: '16px 20px',
marginBottom: 20,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flexWrap: 'wrap',
gap: 12
}}>
<div style={{flex: 1, minWidth: 200}}>
<div style={{
fontWeight: 700,
fontSize: 15,
color: textColor,
marginBottom: 4
}}>
{isUrgent && '⚠️ '}
Deine Trial endet {daysLeft === 1 ? 'morgen' : `in ${daysLeft} Tagen`}
</div>
<div style={{
fontSize: 13,
color: 'var(--text2)',
lineHeight: 1.4
}}>
{isUrgent
? 'Upgrade jetzt um weiterhin alle Features nutzen zu können'
: 'Wähle ein Abo um unbegrenzt Zugriff zu erhalten'
}
</div>
</div>
<Link
to="/settings?tab=subscription"
style={{
padding: '10px 20px',
borderRadius: 8,
background: isUrgent ? '#D85A30' : 'var(--accent)',
color: 'white',
fontWeight: 600,
fontSize: 14,
textDecoration: 'none',
whiteSpace: 'nowrap',
border: 'none',
cursor: 'pointer'
}}
>
{isUrgent ? 'Jetzt upgraden' : 'Abo wählen'}
</Link>
</div>
)
}

View File

@ -8,6 +8,7 @@ import {
import { api } from '../utils/api'
import { useProfile } from '../context/ProfileContext'
import { getBfCategory } from '../utils/calc'
import TrialBanner from '../components/TrialBanner'
import { getInterpretation, getStatusColor, getStatusBg } from '../utils/interpret'
import Markdown from '../utils/Markdown'
import dayjs from 'dayjs'
@ -315,6 +316,9 @@ export default function Dashboard() {
</div>
</div>
{/* Trial Banner */}
<TrialBanner profile={activeProfile}/>
{!hasAnyData && (
<div className="empty-state">
<h3>Willkommen bei Mitai Jinkendo!</h3>