Components
Loading preview...
Here is Input Modal component
@arihantcodes_1f7b8c4d
npx shadcn@latest add https://21st.dev/r/arihantcodes_1f7b8c4d/input-modal'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger } from '@/components/ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Avatar, AvatarImage } from '@/components/ui/avatar';
import { Badge } from '@/components/ui/badge';
import {
Headphones,
X,
FileText,
ChevronDown,
ArrowDownNarrowWide,
Upload,
Link,
FolderOpen,
Search,
Play,
Clock,
Globe,
Youtube,
FileImage,
Copy,
Star,
} from 'lucide-react';
interface Project {
id: string;
name: string;
type: 'pdf' | 'url' | 'audio';
size?: string;
duration?: string;
createdAt: string;
thumbnail?: string;
status: 'completed' | 'processing' | 'draft';
}
const mockProjects: Project[] = [
{
id: '1',
name: 'Future Thinking Talk.pdf',
type: 'pdf',
size: '45.4 KB',
createdAt: '2 hours ago',
status: 'completed',
},
{
id: '2',
name: 'AI Revolution Article',
type: 'url',
duration: '12:34',
createdAt: '1 day ago',
status: 'completed',
},
{
id: '3',
name: 'Tech Trends 2024',
type: 'audio',
duration: '8:45',
createdAt: '3 days ago',
status: 'processing',
},
{
id: '4',
name: 'Climate Change Report',
type: 'pdf',
size: '2.1 MB',
createdAt: '1 week ago',
status: 'draft',
},
];
export default function InputModel() {
const [isOpen, setIsOpen] = useState(true);
const [activeTab, setActiveTab] = useState('upload');
const [urlInput, setUrlInput] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const [selectedProject, setSelectedProject] = useState<string | null>(null);
const [selected, setSelected] = useState('morgan');
const [selected2, setSelected2] = useState('');
const [selected3, setSelected3] = useState('');
const [selected4, setSelected4] = useState('');
const [selected5, setSelected5] = useState('');
const [selected6, setSelected6] = useState('');
const filteredProjects = mockProjects.filter((project) =>
project.name.toLowerCase().includes(searchQuery.toLowerCase()),
);
const getProjectIcon = (type: string) => {
switch (type) {
case 'pdf':
return <FileText className="w-4 h-4" />;
case 'url':
return <Globe className="w-4 h-4" />;
case 'audio':
return <Headphones className="w-4 h-4" />;
default:
return <FileText className="w-4 h-4" />;
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'completed':
return 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400';
case 'processing':
return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/20 dark:text-yellow-400';
case 'draft':
return 'bg-neutral-100 text-neutral-800 dark:bg-neutral-800 dark:text-neutral-400';
default:
return 'bg-neutral-100 text-neutral-800 dark:bg-neutral-800 dark:text-neutral-400';
}
};
if (!isOpen) {
return (
<div className="flex items-center justify-center">
<Button onClick={() => setIsOpen(true)}>Open Audio Show Creator</Button>
</div>
);
}
return (
<div className="flex items-center justify-center p-4 ">
<Card className="w-full max-w-xl max-h-[90vh] overflow-y-auto rounded-3xl shadow-2xl border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-950">
<CardContent className="p-4 sm:p-6 lg:p-8 h-full overflow-y-auto">
<div className="flex items-start justify-between mb-6">
<div className="flex gap-3 sm:gap-4 flex-1">
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-neutral-800 dark:bg-neutral-700 rounded-2xl flex items-center justify-center flex-shrink-0">
<Headphones className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
</div>
<div className="flex-1 min-w-0">
<h1 className="text-base sm:text-lg font-semibold mb-2 text-neutral-900 dark:text-neutral-100">
Create Your Audio Show
</h1>
<p className="text-neutral-600 dark:text-neutral-400 text-sm leading-relaxed font-normal">
Drop a document or paste a link — GenFM will instantly turn it into a fully voiced
podcast you can preview, edit, and download.
</p>
</div>
</div>
<Button
variant="ghost"
size="icon"
onClick={() => setIsOpen(false)}
className="text-neutral-400 hover:text-neutral-600 dark:text-neutral-500 dark:hover:text-neutral-300 -mt-2 -mr-2 flex-shrink-0"
>
<X className="w-5 h-5" />
</Button>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab} className="mb-6 sm:mb-8">
<TabsList className="grid w-full grid-cols-3 rounded-xl p-1 bg-neutral-100 dark:bg-neutral-800">
<TabsTrigger
value="upload"
className="rounded-lg font-medium text-xs sm:text-sm data-[state=active]:bg-white data-[state=active]:text-neutral-900 dark:data-[state=active]:bg-neutral-700 dark:data-[state=active]:text-neutral-100"
>
<Upload className="w-4 h-4 mr-1 sm:mr-2" />
<span className="hidden sm:inline">Upload File</span>
<span className="sm:hidden">Upload</span>
</TabsTrigger>
<TabsTrigger
value="url"
className="rounded-lg font-medium text-xs sm:text-sm data-[state=active]:bg-white data-[state=active]:text-neutral-900 dark:data-[state=active]:bg-neutral-700 dark:data-[state=active]:text-neutral-100"
>
<Link className="w-4 h-4 mr-1 sm:mr-2" />
<span className="hidden sm:inline">Import via URL</span>
<span className="sm:hidden">URL</span>
</TabsTrigger>
<TabsTrigger
value="existing"
className="rounded-lg font-medium text-xs sm:text-sm data-[state=active]:bg-white data-[state=active]:text-neutral-900 dark:data-[state=active]:bg-neutral-700 dark:data-[state=active]:text-neutral-100"
>
<FolderOpen className="w-4 h-4 mr-1 sm:mr-2" />
<span className="hidden sm:inline">Choose Existing</span>
<span className="sm:hidden">Existing</span>
</TabsTrigger>
</TabsList>
<TabsContent value="upload" className="mt-6">
<div className="border-2 border-dashed border-neutral-300 dark:border-neutral-700 rounded-xl p-8 text-center bg-neutral-50 dark:bg-neutral-900/50">
<div className="w-12 h-12 bg-neutral-200 dark:bg-neutral-800 rounded-full flex items-center justify-center mx-auto mb-4">
<Upload className="w-6 h-6 text-neutral-600 dark:text-neutral-400" />
</div>
<h3 className="text-lg font-medium mb-2 text-neutral-900 dark:text-neutral-100">
Drop your file here
</h3>
<p className="text-neutral-600 dark:text-neutral-400 mb-4">
Support for PDF, DOCX, TXT files up to 10MB
</p>
<Button
variant="outline"
className="border-neutral-300 dark:border-neutral-700 bg-transparent"
>
Browse Files
</Button>
</div>
</TabsContent>
<TabsContent value="url" className="mt-6">
<div className="space-y-4">
<div>
<Label
htmlFor="url-input"
className="text-sm font-medium text-neutral-900 dark:text-neutral-100"
>
Content URL
</Label>
<div className="mt-2">
<Input
id="url-input"
type="url"
placeholder="https://example.com/article or YouTube URL"
value={urlInput}
onChange={(e) => setUrlInput(e.target.value)}
className="w-full h-12 rounded-xl border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900"
/>
</div>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
<div className="flex items-center gap-2 p-3 rounded-lg border border-neutral-200 dark:border-neutral-700 bg-neutral-50 dark:bg-neutral-900/50">
<Globe className="w-4 h-4 text-neutral-600 dark:text-neutral-400" />
<span className="text-xs text-neutral-600 dark:text-neutral-400">Articles</span>
</div>
<div className="flex items-center gap-2 p-3 rounded-lg border border-neutral-200 dark:border-neutral-700 bg-neutral-50 dark:bg-neutral-900/50">
<Youtube className="w-4 h-4 text-neutral-600 dark:text-neutral-400" />
<span className="text-xs text-neutral-600 dark:text-neutral-400">YouTube</span>
</div>
<div className="flex items-center gap-2 p-3 rounded-lg border border-neutral-200 dark:border-neutral-700 bg-neutral-50 dark:bg-neutral-900/50">
<FileText className="w-4 h-4 text-neutral-600 dark:text-neutral-400" />
<span className="text-xs text-neutral-600 dark:text-neutral-400">Blogs</span>
</div>
<div className="flex items-center gap-2 p-3 rounded-lg border border-neutral-200 dark:border-neutral-700 bg-neutral-50 dark:bg-neutral-900/50">
<FileImage className="w-4 h-4 text-neutral-600 dark:text-neutral-400" />
<span className="text-xs text-neutral-600 dark:text-neutral-400">News</span>
</div>
</div>
{urlInput && (
<div className="p-4 rounded-xl border border-neutral-200 dark:border-neutral-700 bg-neutral-50 dark:bg-neutral-900/50">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-neutral-200 dark:bg-neutral-800 rounded-lg flex items-center justify-center">
<Globe className="w-4 h-4 text-neutral-600 dark:text-neutral-400" />
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-neutral-900 dark:text-neutral-100 truncate">
Ready to import content
</p>
<p className="text-xs text-neutral-600 dark:text-neutral-400 truncate">
{urlInput}
</p>
</div>
</div>
</div>
)}
</div>
</TabsContent>
<TabsContent value="existing" className="mt-6">
<div className="space-y-4">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-neutral-400" />
<Input
placeholder="Search projects..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 h-12 rounded-xl border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900"
/>
</div>
<div className="max-h-64 overflow-y-auto space-y-2">
{filteredProjects.map((project) => (
<div
key={project.id}
onClick={() => setSelectedProject(project.id)}
className={`p-4 rounded-xl border cursor-pointer transition-all ${
selectedProject === project.id
? 'border-neutral-400 dark:border-neutral-600 bg-neutral-100 dark:bg-neutral-800'
: 'border-neutral-200 dark:border-neutral-700 hover:border-neutral-300 dark:hover:border-neutral-600 bg-white dark:bg-neutral-900/50'
}`}
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-neutral-200 dark:bg-neutral-800 rounded-lg flex items-center justify-center">
{getProjectIcon(project.type)}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<p className="text-sm font-medium text-neutral-900 dark:text-neutral-100 truncate">
{project.name}
</p>
<Badge
className={`text-xs px-2 py-0.5 ${getStatusColor(project.status)}`}
>
{project.status}
</Badge>
</div>
<div className="flex items-center gap-4 text-xs text-neutral-600 dark:text-neutral-400">
<span className="flex items-center gap-1">
<Clock className="w-3 h-3" />
{project.createdAt}
</span>
{project.size && <span>{project.size}</span>}
{project.duration && (
<span className="flex items-center gap-1">
<Play className="w-3 h-3" />
{project.duration}
</span>
)}
</div>
</div>
<div className="flex items-center gap-1">
<Button variant="ghost" size="icon" className="w-8 h-8">
<Copy className="w-3 h-3" />
</Button>
<Button variant="ghost" size="icon" className="w-8 h-8">
<Star className="w-3 h-3" />
</Button>
</div>
</div>
</div>
))}
</div>
</div>
</TabsContent>
</Tabs>
<div className="space-y-6">
<div>
<Label className="block text-sm font-medium mb-3 text-neutral-900 dark:text-neutral-100">
Format Style
</Label>
<Select defaultValue="default" onValueChange={setSelected5}>
<SelectTrigger className="w-full border-neutral-300 dark:border-neutral-700 rounded-xl h-12 bg-white dark:bg-neutral-900">
<div className="flex items-center justify-between w-full">
<span className="font-medium text-neutral-900 dark:text-neutral-100">
{selected5 || 'Interview Mode'}
</span>
<div className="bg-neutral-100 dark:bg-neutral-800 text-neutral-700 dark:text-neutral-300 px-2 py-1 mr-2 rounded-md text-xs">
Default
</div>
</div>
</SelectTrigger>
<SelectContent>
<SelectItem value="default">Interview Mode</SelectItem>
<SelectItem value="narrative">Narrative Style</SelectItem>
<SelectItem value="discussion">Panel Discussion</SelectItem>
<SelectItem value="monologue">Solo Monologue</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6">
<div>
<Label className="block text-sm font-medium mb-3 text-neutral-900 dark:text-neutral-100">
Host Voice
</Label>
<Select defaultValue="alex" onValueChange={setSelected2}>
<SelectTrigger className="w-full border-neutral-300 dark:border-neutral-700 rounded-xl h-12 bg-white dark:bg-neutral-900">
<div className="flex items-center gap-3">
<Avatar className="w-6 h-6">
<AvatarImage src="/voice1.png" alt="Alex" />
</Avatar>
<span className="font-medium text-neutral-900 dark:text-neutral-100">
{selected2 || 'alex'}
</span>
</div>
</SelectTrigger>
<SelectContent>
<SelectItem value="alex">Alex</SelectItem>
<SelectItem value="sarah">Sarah</SelectItem>
<SelectItem value="mike">Mike</SelectItem>
<SelectItem value="emma">Emma</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label className="block text-sm font-medium mb-3 text-neutral-900 dark:text-neutral-100">
Guest Voice
</Label>
<Select defaultValue="morgan" onValueChange={setSelected}>
<SelectTrigger className="w-full border-neutral-300 dark:border-neutral-700 rounded-xl h-12 bg-white dark:bg-neutral-900">
<div className="flex items-center gap-3">
<Avatar className="w-6 h-6">
<AvatarImage src="/voice2.png" alt="Morgan" />
</Avatar>
<span className="font-medium text-neutral-900 dark:text-neutral-100">
{selected}
</span>
</div>
</SelectTrigger>
<SelectContent>
<SelectItem value="morgan">Morgan</SelectItem>
<SelectItem value="jordan">Jordan</SelectItem>
<SelectItem value="taylor">Taylor</SelectItem>
<SelectItem value="casey">Casey</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6">
<div>
<Label className="block text-sm font-medium mb-3 text-neutral-900 dark:text-neutral-100">
Voice Engine
</Label>
<Select defaultValue="eleven-v2" onValueChange={setSelected3}>
<SelectTrigger className="w-full border-neutral-300 dark:border-neutral-700 rounded-xl h-12 bg-white dark:bg-neutral-900">
<div className="flex items-center justify-between w-full">
<span className="font-medium text-neutral-900 dark:text-neutral-100">
{selected3 || 'Eleven AI v2'}
</span>
<div className="bg-neutral-100 dark:bg-neutral-800 text-neutral-700 dark:text-neutral-300 px-2 py-1 mr-2 rounded-md text-xs">
Multilingual
</div>
</div>
</SelectTrigger>
<SelectContent>
<SelectItem value="eleven-v2">Eleven AI v2</SelectItem>
<SelectItem value="eleven-v1">Eleven AI v1</SelectItem>
<SelectItem value="openai">OpenAI TTS</SelectItem>
<SelectItem value="azure">Azure Speech</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label className="block text-sm font-medium mb-3 text-neutral-900 dark:text-neutral-100">
Language
</Label>
<Select defaultValue="auto" onValueChange={setSelected4}>
<SelectTrigger className="w-full border-neutral-300 dark:border-neutral-700 rounded-xl h-12 bg-white dark:bg-neutral-900">
<span className="font-medium text-neutral-900 dark:text-neutral-100">
{selected4 || 'Auto-detect'}
</span>
</SelectTrigger>
<SelectContent>
<SelectItem value="auto">Auto-detect</SelectItem>
<SelectItem value="english">English</SelectItem>
<SelectItem value="spanish">Spanish</SelectItem>
<SelectItem value="french">French</SelectItem>
<SelectItem value="german">German</SelectItem>
<SelectItem value="italian">Italian</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div>
<Label className="block text-sm font-medium mb-3 text-neutral-900 dark:text-neutral-100">
Audio Quality
</Label>
<Select defaultValue="studio" onValueChange={setSelected6}>
<SelectTrigger className="w-full border-neutral-300 dark:border-neutral-700 rounded-xl h-12 bg-white dark:bg-neutral-900">
<span className="font-medium text-neutral-900 dark:text-neutral-100">
{selected6 || 'Studio Quality'}
</span>
</SelectTrigger>
<SelectContent>
<SelectItem value="studio">Studio Quality</SelectItem>
<SelectItem value="high">High Quality</SelectItem>
<SelectItem value="standard">Standard Quality</SelectItem>
<SelectItem value="compressed">Compressed</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="flex flex-col sm:flex-row justify-end gap-3 sm:gap-5 mt-8 pt-6 border-t border-neutral-200 dark:border-neutral-800">
<Button
variant="outline"
className="rounded-xl px-4 sm:px-6 h-12 border-neutral-300 dark:border-neutral-700 bg-transparent text-sm flex items-center justify-center order-2 sm:order-1"
>
<ArrowDownNarrowWide className="h-4 w-4 mr-2" />
Recent
<ChevronDown className="w-4 h-4 ml-2" />
</Button>
<Button className="bg-neutral-900 hover:bg-neutral-800 dark:bg-neutral-100 dark:hover:bg-neutral-200 dark:text-neutral-900 text-white rounded-xl px-6 sm:px-8 h-12 font-medium order-1 sm:order-2">
Generate
</Button>
</div>
</CardContent>
</Card>
</div>
);
}