Files
breakpilot-core/pitch-deck/components/slides/TheAskSlide.tsx
Benjamin Boenisch b7d21daa24
All checks were successful
CI / test-bqas (push) Successful in 32s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 46s
CI / test-python-voice (push) Successful in 38s
feat: Add DevSecOps tools, Woodpecker proxy, Vault persistent storage, pitch-deck annex slides
- Install Gitleaks, Trivy, Grype, Syft, Semgrep, Bandit in backend-core Dockerfile
- Add Woodpecker SQLite proxy API (fallback without API token)
- Mount woodpecker_data volume read-only to backend-core
- Add backend proxy fallback in admin-core Woodpecker route
- Add Vault file-based persistent storage (config.hcl, init-vault.sh)
- Auto-init, unseal and root-token persistence for Vault
- Add 6 pitch-deck annex slides (Assumptions, Architecture, GTM, Regulatory, Engineering, AI Pipeline)
- Dynamic margin/amortization KPIs in BusinessModelSlide
- Market sources modal with citations in MarketSlide
- Redesign nginx landing page to 3-column layout (Lehrer/Compliance/Core)
- Extend MkDocs nav with Services and SDK documentation sections
- Add SDK Protection architecture doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:42:43 +01:00

153 lines
5.8 KiB
TypeScript

'use client'
import { motion } from 'framer-motion'
import { Language, PitchFunding } from '@/lib/types'
import { t } from '@/lib/i18n'
import GradientText from '../ui/GradientText'
import FadeInView from '../ui/FadeInView'
import AnimatedCounter from '../ui/AnimatedCounter'
import GlassCard from '../ui/GlassCard'
import { Target, Calendar, FileText } from 'lucide-react'
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts'
interface TheAskSlideProps {
lang: Language
funding: PitchFunding
}
const COLORS = ['#6366f1', '#a78bfa', '#60a5fa', '#34d399', '#fbbf24']
function formatFundingAmount(amount: number): { target: number; suffix: string } {
if (amount >= 1_000_000) {
return { target: Math.round(amount / 100_000) / 10, suffix: ' Mio.' }
}
if (amount >= 1_000) {
return { target: Math.round(amount / 1_000), suffix: 'k' }
}
return { target: amount, suffix: '' }
}
function formatTargetDate(dateStr: string, lang: Language): string {
if (!dateStr) return 'TBD'
try {
const d = new Date(dateStr)
const quarter = Math.ceil((d.getMonth() + 1) / 3)
return `Q${quarter} ${d.getFullYear()}`
} catch {
return dateStr
}
}
export default function TheAskSlide({ lang, funding }: TheAskSlideProps) {
const i = t(lang)
const useOfFunds = funding?.use_of_funds || []
const amount = funding?.amount_eur || 0
const { target, suffix } = formatFundingAmount(amount)
const pieData = useOfFunds.map((item) => ({
name: lang === 'de' ? item.label_de : item.label_en,
value: item.percentage,
}))
return (
<div>
<FadeInView className="text-center mb-10">
<h2 className="text-4xl md:text-5xl font-bold mb-3">
<GradientText>{i.theAsk.title}</GradientText>
</h2>
<p className="text-lg text-white/50 max-w-2xl mx-auto">{i.theAsk.subtitle}</p>
</FadeInView>
{/* Main Number — dynamisch aus funding.amount_eur */}
<FadeInView delay={0.2} className="text-center mb-10">
<motion.div
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ delay: 0.4, type: 'spring', stiffness: 200 }}
>
<p className="text-6xl md:text-8xl font-bold text-white mb-2">
<AnimatedCounter target={target} suffix={suffix} duration={2000} decimals={suffix === ' Mio.' ? 1 : 0} />
<span className="text-3xl md:text-4xl text-white/50 ml-2">EUR</span>
</p>
</motion.div>
</FadeInView>
{/* Details — dynamisch aus funding-Objekt */}
<div className="grid md:grid-cols-3 gap-4 mb-8">
<GlassCard delay={0.5} className="text-center p-5">
<FileText className="w-6 h-6 text-indigo-400 mx-auto mb-2" />
<p className="text-xs text-white/40 mb-1">{i.theAsk.instrument}</p>
<p className="text-lg font-bold text-white">{funding?.instrument || 'SAFE'}</p>
</GlassCard>
<GlassCard delay={0.6} className="text-center p-5">
<Calendar className="w-6 h-6 text-purple-400 mx-auto mb-2" />
<p className="text-xs text-white/40 mb-1">{i.theAsk.targetDate}</p>
<p className="text-lg font-bold text-white">
{formatTargetDate(funding?.target_date, lang)}
</p>
</GlassCard>
<GlassCard delay={0.7} className="text-center p-5">
<Target className="w-6 h-6 text-blue-400 mx-auto mb-2" />
<p className="text-xs text-white/40 mb-1">{lang === 'de' ? 'Runde' : 'Round'}</p>
<p className="text-lg font-bold text-white">{funding?.round_name || 'Pre-Seed'}</p>
</GlassCard>
</div>
{/* Use of Funds */}
<FadeInView delay={0.8}>
<GlassCard hover={false} className="p-6">
<h3 className="text-lg font-semibold text-white mb-4 text-center">{i.theAsk.useOfFunds}</h3>
<div className="flex flex-col md:flex-row items-center gap-8">
{/* Pie Chart */}
<div className="w-48 h-48">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={pieData}
cx="50%"
cy="50%"
innerRadius={50}
outerRadius={80}
dataKey="value"
stroke="none"
>
{pieData.map((_, idx) => (
<Cell key={idx} fill={COLORS[idx % COLORS.length]} />
))}
</Pie>
<Tooltip
contentStyle={{
background: 'rgba(10, 10, 26, 0.9)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 8,
color: '#fff',
fontSize: 13,
}}
formatter={(value: number) => `${value}%`}
/>
</PieChart>
</ResponsiveContainer>
</div>
{/* Legend */}
<div className="flex-1 space-y-3">
{useOfFunds.map((item, idx) => (
<div key={idx} className="flex items-center gap-3">
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: COLORS[idx] }} />
<span className="flex-1 text-sm text-white/70">
{lang === 'de' ? item.label_de : item.label_en}
</span>
<span className="text-sm font-bold text-white">{item.percentage}%</span>
<span className="text-xs text-white/30">
{((amount * item.percentage) / 100).toLocaleString('de-DE')} EUR
</span>
</div>
))}
</div>
</div>
</GlassCard>
</FadeInView>
</div>
)
}