Video_AdGenesis_App / frontend /src /components /ProviderSelect.tsx
sushilideaclan01's picture
Enhance prompt validation and safety features
82a1419
Raw History Blame Contribute Delete
9.65 kB
import React from 'react';
import { motion } from 'framer-motion';
import { VideoProvider } from '@/types';
import { ZapIcon, BrainIcon, CheckIcon } from './Icons';
type PricingSamples = { '1s'?: number; '8s'?: number; '30s'?: number; '60s'?: number };
interface ProviderCard {
id: VideoProvider;
name: string;
subtitle?: string;
description?: string;
icon: React.FC<any>;
color: 'coral' | 'electric';
features: string[];
badge?: string;
pricing?: { per_second?: number | null; samples?: PricingSamples; evidence?: string | null };
}
interface ProviderSelectProps {
onSelect: (provider: VideoProvider) => void;
}
const providers: ProviderCard[] = [
{
id: 'kling' as VideoProvider,
name: 'Kie API',
subtitle: '(cheap use less credits)',
description: 'High-quality video generation with advanced continuity. Perfect for professional UGC and talking head videos.',
icon: ZapIcon,
color: 'coral',
features: [
'Veo 3.1 Fast Model',
'Image-to-Video & Text-to-Video',
'Video Extension (Seamless)',
'GPT-4o Prompt Generation',
'9:16 Portrait Format',
'Voice Type Selection',
],
badge: 'Recommended',
pricing: {
per_second: 0.05,
samples: {
'1s': 0.05,
'8s': 0.4,
'30s': 1.5,
'60s': 3.0,
},
evidence: 'Veo 3 Fast (8s, with audio) — Kie.ai $0.40 (≈$0.05/sec)',
},
},
{
id: 'replicate' as VideoProvider,
name: 'Replicate API',
subtitle: '(more credits, more expensive)',
description: 'Access to diverse video generation models. Great for experimentation and unique creative styles.',
icon: BrainIcon,
color: 'electric',
features: [
'Multiple Model Options',
'Flexible Duration',
'Creative Styles',
'Cost Effective',
'API Simplicity',
'Community Models',
],
badge: 'Flexible',
pricing: {
per_second: 0.15,
samples: {
'1s': 0.15,
'8s': 1.2,
'30s': 4.5,
'60s': 9.0,
},
evidence: 'google/veo-3-fast with_audio — $0.15 per second',
},
},
];
export const ProviderSelect: React.FC<ProviderSelectProps> = ({ onSelect }) => {
return (
<div className="min-h-[70vh] flex flex-col items-center justify-center p-8">
{/* Hero Section */}
<motion.div
className="text-center mb-16"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<h1 className="text-5xl md:text-6xl font-display font-bold mb-4">
<span className="gradient-text">Video AdGenesis</span>
<span className="text-void-200"> Studio</span>
</h1>
<p className="text-xl text-void-400 max-w-2xl mx-auto">
Transform your scripts into stunning videos with AI. Choose your preferred generation engine to get started.
</p>
</motion.div>
{/* Provider Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-5xl w-full">
{providers.map((provider, index) => (
<motion.div
key={provider.id}
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.15 }}
>
<button
onClick={() => onSelect(provider.id)}
className={`
w-full text-left provider-card card-interactive
${provider.color === 'coral' ? 'hover:border-coral-500/50 hover:glow-coral' : 'hover:border-electric-500/50 hover:glow-electric'}
group transition-all duration-500
`}
>
{/* Badge */}
<div className="flex items-start justify-between mb-6">
<div className={`
w-16 h-16 rounded-2xl flex items-center justify-center
${provider.color === 'coral'
? 'bg-coral-500/10 text-coral-400 group-hover:bg-coral-500/20'
: 'bg-electric-500/10 text-electric-400 group-hover:bg-electric-500/20'
}
transition-colors duration-300
`}>
<provider.icon size={32} />
</div>
<span className={`
px-3 py-1 rounded-full text-xs font-semibold
${provider.color === 'coral'
? 'bg-coral-500/20 text-coral-300'
: 'bg-electric-500/20 text-electric-300'
}
`}>
{provider.badge}
</span>
</div>
{/* Title & Description */}
<h2 className="text-2xl font-display font-bold text-void-100 mb-1">
{provider.name}
</h2>
<p className={`text-sm mb-3 ${provider.color === 'coral' ? 'text-coral-400' : 'text-electric-400'}`}>
{provider.subtitle}
</p>
<p className="text-void-400 text-sm mb-6 leading-relaxed">
{provider.description}
</p>
{/* Pricing (per sec, 8s, 30s, 1m) */}
{provider.pricing?.per_second != null && (
<div className="mt-4 mb-4">
<div className={`
rounded-xl p-4
${provider.color === 'coral'
? 'bg-gradient-to-br from-coral-500/5 to-coral-600/10 border border-coral-500/20'
: 'bg-gradient-to-br from-electric-500/5 to-electric-600/10 border border-electric-500/20'
}
`}>
<div className="grid grid-cols-4 gap-3">
{(['1s', '8s', '30s', '60s'] as Array<keyof PricingSamples>).map((k) => {
const v = provider.pricing?.samples?.[k as keyof typeof provider.pricing.samples];
return (
<div
key={k}
className={`
flex flex-col items-center justify-center p-3 rounded-lg
${provider.color === 'coral'
? 'bg-void-900/40 hover:bg-void-800/60'
: 'bg-void-900/40 hover:bg-void-800/60'
}
transition-all duration-300 group/price
`}
>
<div className={`
text-[10px] uppercase font-semibold tracking-wider mb-1
${provider.color === 'coral' ? 'text-coral-400/70' : 'text-electric-400/70'}
group-hover/price:opacity-100 transition-opacity
`}>
{k}
</div>
<div className={`
text-xl md:text-2xl font-bold leading-none
${provider.color === 'coral'
? 'text-coral-300 group-hover/price:text-coral-200'
: 'text-electric-300 group-hover/price:text-electric-200'
}
transition-colors duration-300
`}>
{v != null ? `$${v.toFixed(2)}` : '—'}
</div>
</div>
);
})}
</div>
<div className="mt-3 pt-3 border-t border-void-700/30 flex items-center justify-center">
<span className="text-[10px] text-void-500 uppercase tracking-wider">
Per second rate: ${provider.pricing.per_second?.toFixed(4) || '—'}
</span>
</div>
</div>
</div>
)}
{/* Features */}
<div className="grid grid-cols-2 gap-2 mt-6">
{provider.features.map((feature, i) => (
<div key={i} className="flex items-center gap-2">
<CheckIcon
size={14}
className={provider.color === 'coral' ? 'text-coral-400' : 'text-electric-400'}
/>
<span className="text-xs text-void-300">{feature}</span>
</div>
))}
</div>
{/* CTA Arrow */}
<div className={`
mt-6 flex items-center gap-2 text-sm font-semibold
${provider.color === 'coral' ? 'text-coral-400' : 'text-electric-400'}
group-hover:gap-4 transition-all duration-300
`}>
<span>Get Started</span>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
</button>
</motion.div>
))}
</div>
{/* Footer Note */}
<motion.p
className="mt-12 text-void-500 text-sm text-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.8 }}
>
Both providers use your Python backend. API keys are configured server-side.
</motion.p>
</div>
);
};