mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-16 21:30:39 -07:00
149 lines
5.0 KiB
TypeScript
149 lines
5.0 KiB
TypeScript
import { HugeiconsIcon } from '@hugeicons/react';
|
|
import { Mic01Icon, PauseIcon, PlayIcon, Upload01Icon } from '@hugeicons/core-free-icons';
|
|
import { useRef, useState } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { FormControl, FormItem, FormMessage } from '@/components/ui/form';
|
|
|
|
interface AudioSampleUploadProps {
|
|
file: File | null | undefined;
|
|
onFileChange: (file: File | undefined) => void;
|
|
onTranscribe: () => void;
|
|
onPlayPause: () => void;
|
|
isPlaying: boolean;
|
|
isValidating?: boolean;
|
|
isTranscribing?: boolean;
|
|
isDisabled?: boolean;
|
|
fieldName: string;
|
|
}
|
|
|
|
export function AudioSampleUpload({
|
|
file,
|
|
onFileChange,
|
|
onTranscribe,
|
|
onPlayPause,
|
|
isPlaying,
|
|
isValidating = false,
|
|
isTranscribing = false,
|
|
isDisabled = false,
|
|
fieldName,
|
|
}: AudioSampleUploadProps) {
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
return (
|
|
<FormItem>
|
|
<FormControl>
|
|
<div className="flex flex-col gap-2">
|
|
<input
|
|
type="file"
|
|
accept="audio/*"
|
|
name={fieldName}
|
|
ref={fileInputRef}
|
|
onChange={(e) => {
|
|
const selectedFile = e.target.files?.[0];
|
|
if (selectedFile) {
|
|
onFileChange(selectedFile);
|
|
} else {
|
|
onFileChange(undefined);
|
|
}
|
|
}}
|
|
className="hidden"
|
|
/>
|
|
<div
|
|
role="button"
|
|
tabIndex={0}
|
|
onDragOver={(e) => {
|
|
e.preventDefault();
|
|
setIsDragging(true);
|
|
}}
|
|
onDragLeave={(e) => {
|
|
e.preventDefault();
|
|
setIsDragging(false);
|
|
}}
|
|
onDrop={(e) => {
|
|
e.preventDefault();
|
|
setIsDragging(false);
|
|
const droppedFile = e.dataTransfer.files?.[0];
|
|
if (droppedFile?.type.startsWith('audio/')) {
|
|
onFileChange(droppedFile);
|
|
}
|
|
}}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault();
|
|
fileInputRef.current?.click();
|
|
}
|
|
}}
|
|
className={`flex flex-col items-center justify-center gap-4 p-4 border-2 rounded-lg transition-colors min-h-[180px] ${
|
|
file
|
|
? 'border-primary bg-primary/5'
|
|
: isDragging
|
|
? 'border-primary bg-primary/5'
|
|
: 'border-dashed border-muted-foreground/25 hover:border-muted-foreground/50'
|
|
}`}
|
|
>
|
|
{!file ? (
|
|
<>
|
|
<Button
|
|
type="button"
|
|
size="lg"
|
|
onClick={() => fileInputRef.current?.click()}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<HugeiconsIcon icon={Upload01Icon} size={20} className="h-5 w-5" />
|
|
Choose File
|
|
</Button>
|
|
<p className="text-sm text-muted-foreground text-center">
|
|
Click to choose a file or drag and drop. Maximum duration: 30 seconds.
|
|
</p>
|
|
</>
|
|
) : (
|
|
<>
|
|
<div className="flex items-center gap-2">
|
|
<HugeiconsIcon icon={Upload01Icon} size={20} className="h-5 w-5 text-primary" />
|
|
<span className="font-medium">File uploaded</span>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground text-center">File: {file.name}</p>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
type="button"
|
|
size="icon"
|
|
variant="outline"
|
|
onClick={onPlayPause}
|
|
disabled={isValidating}
|
|
>
|
|
{isPlaying ? <HugeiconsIcon icon={PauseIcon} size={16} className="h-4 w-4" /> : <HugeiconsIcon icon={PlayIcon} size={16} className="h-4 w-4" />}
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={onTranscribe}
|
|
disabled={isTranscribing || isValidating || isDisabled}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<HugeiconsIcon icon={Mic01Icon} size={16} className="h-4 w-4" />
|
|
{isTranscribing ? 'Transcribing...' : 'Transcribe'}
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => {
|
|
onFileChange(undefined);
|
|
if (fileInputRef.current) {
|
|
fileInputRef.current.value = '';
|
|
}
|
|
}}
|
|
>
|
|
Remove
|
|
</Button>
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
);
|
|
}
|