feat: add pitch-deck service to core infrastructure
Migrated pitch-deck from breakpilot-pwa to breakpilot-core. Container: bp-core-pitch-deck on port 3012. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
171
pitch-deck/components/PitchDeck.tsx
Normal file
171
pitch-deck/components/PitchDeck.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useState } from 'react'
|
||||
import { AnimatePresence } from 'framer-motion'
|
||||
import { useSlideNavigation } from '@/lib/hooks/useSlideNavigation'
|
||||
import { useKeyboard } from '@/lib/hooks/useKeyboard'
|
||||
import { usePitchData } from '@/lib/hooks/usePitchData'
|
||||
import { Language, PitchData } from '@/lib/types'
|
||||
|
||||
import ParticleBackground from './ParticleBackground'
|
||||
import ProgressBar from './ProgressBar'
|
||||
import NavigationControls from './NavigationControls'
|
||||
import NavigationFAB from './NavigationFAB'
|
||||
import ChatFAB from './ChatFAB'
|
||||
import SlideOverview from './SlideOverview'
|
||||
import SlideContainer from './SlideContainer'
|
||||
|
||||
import CoverSlide from './slides/CoverSlide'
|
||||
import ProblemSlide from './slides/ProblemSlide'
|
||||
import SolutionSlide from './slides/SolutionSlide'
|
||||
import ProductSlide from './slides/ProductSlide'
|
||||
import HowItWorksSlide from './slides/HowItWorksSlide'
|
||||
import MarketSlide from './slides/MarketSlide'
|
||||
import BusinessModelSlide from './slides/BusinessModelSlide'
|
||||
import TractionSlide from './slides/TractionSlide'
|
||||
import CompetitionSlide from './slides/CompetitionSlide'
|
||||
import TeamSlide from './slides/TeamSlide'
|
||||
import FinancialsSlide from './slides/FinancialsSlide'
|
||||
import TheAskSlide from './slides/TheAskSlide'
|
||||
import AIQASlide from './slides/AIQASlide'
|
||||
|
||||
interface PitchDeckProps {
|
||||
lang: Language
|
||||
onToggleLanguage: () => void
|
||||
}
|
||||
|
||||
export default function PitchDeck({ lang, onToggleLanguage }: PitchDeckProps) {
|
||||
const { data, loading, error } = usePitchData()
|
||||
const nav = useSlideNavigation()
|
||||
const [fabOpen, setFabOpen] = useState(false)
|
||||
|
||||
const toggleFullscreen = useCallback(() => {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen()
|
||||
} else {
|
||||
document.exitFullscreen()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const toggleMenu = useCallback(() => {
|
||||
setFabOpen(prev => !prev)
|
||||
}, [])
|
||||
|
||||
useKeyboard({
|
||||
onNext: nav.nextSlide,
|
||||
onPrev: nav.prevSlide,
|
||||
onFirst: nav.goToFirst,
|
||||
onLast: nav.goToLast,
|
||||
onOverview: nav.toggleOverview,
|
||||
onFullscreen: toggleFullscreen,
|
||||
onLanguageToggle: onToggleLanguage,
|
||||
onMenuToggle: toggleMenu,
|
||||
onGoToSlide: nav.goToSlide,
|
||||
enabled: !nav.showOverview,
|
||||
})
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="h-screen flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
|
||||
<p className="text-white/40 text-sm">{lang === 'de' ? 'Lade Pitch-Daten...' : 'Loading pitch data...'}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="h-screen flex items-center justify-center">
|
||||
<div className="text-center max-w-md">
|
||||
<p className="text-red-400 mb-2">{lang === 'de' ? 'Fehler beim Laden' : 'Loading error'}</p>
|
||||
<p className="text-white/40 text-sm">{error || 'No data'}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function renderSlide() {
|
||||
if (!data) return null
|
||||
|
||||
switch (nav.currentSlide) {
|
||||
case 'cover':
|
||||
return <CoverSlide lang={lang} onNext={nav.nextSlide} />
|
||||
case 'problem':
|
||||
return <ProblemSlide lang={lang} />
|
||||
case 'solution':
|
||||
return <SolutionSlide lang={lang} />
|
||||
case 'product':
|
||||
return <ProductSlide lang={lang} products={data.products} />
|
||||
case 'how-it-works':
|
||||
return <HowItWorksSlide lang={lang} />
|
||||
case 'market':
|
||||
return <MarketSlide lang={lang} market={data.market} />
|
||||
case 'business-model':
|
||||
return <BusinessModelSlide lang={lang} products={data.products} />
|
||||
case 'traction':
|
||||
return <TractionSlide lang={lang} milestones={data.milestones} metrics={data.metrics} />
|
||||
case 'competition':
|
||||
return <CompetitionSlide lang={lang} features={data.features} competitors={data.competitors} />
|
||||
case 'team':
|
||||
return <TeamSlide lang={lang} team={data.team} />
|
||||
case 'financials':
|
||||
return <FinancialsSlide lang={lang} />
|
||||
case 'the-ask':
|
||||
return <TheAskSlide lang={lang} funding={data.funding} />
|
||||
case 'ai-qa':
|
||||
return <AIQASlide lang={lang} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen relative overflow-hidden bg-gradient-to-br from-slate-950 via-[#0a0a1a] to-slate-950">
|
||||
<ParticleBackground />
|
||||
<ProgressBar current={nav.currentIndex} total={nav.totalSlides} />
|
||||
|
||||
<SlideContainer slideKey={nav.currentSlide} direction={nav.direction}>
|
||||
{renderSlide()}
|
||||
</SlideContainer>
|
||||
|
||||
<NavigationControls
|
||||
onPrev={nav.prevSlide}
|
||||
onNext={nav.nextSlide}
|
||||
isFirst={nav.isFirst}
|
||||
isLast={nav.isLast}
|
||||
current={nav.currentIndex}
|
||||
total={nav.totalSlides}
|
||||
/>
|
||||
|
||||
<ChatFAB
|
||||
lang={lang}
|
||||
currentSlide={nav.currentSlide}
|
||||
currentIndex={nav.currentIndex}
|
||||
visitedSlides={nav.visitedSlides}
|
||||
onGoToSlide={nav.goToSlide}
|
||||
/>
|
||||
|
||||
<NavigationFAB
|
||||
currentIndex={nav.currentIndex}
|
||||
totalSlides={nav.totalSlides}
|
||||
visitedSlides={nav.visitedSlides}
|
||||
onGoToSlide={nav.goToSlide}
|
||||
lang={lang}
|
||||
onToggleLanguage={onToggleLanguage}
|
||||
/>
|
||||
|
||||
<AnimatePresence>
|
||||
{nav.showOverview && (
|
||||
<SlideOverview
|
||||
currentIndex={nav.currentIndex}
|
||||
onGoToSlide={nav.goToSlide}
|
||||
onClose={() => nav.setShowOverview(false)}
|
||||
lang={lang}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user