refactor: enhance FileOrUrlField with reusable UI components and existing file support

This commit is contained in:
rajun 2026-07-28 11:02:18 +05:30
parent e9aad1afe3
commit d329bd7789

View File

@ -1,4 +1,6 @@
import { Upload, Link, X } from 'lucide-react'; import { Upload, Link, X } from 'lucide-react';
import { Input } from '~/components/ui/input';
import { Label } from '~/components/ui/label';
export type InputMode = 'url' | 'file'; export type InputMode = 'url' | 'file';
@ -14,21 +16,24 @@ export interface FileOrUrlFieldProps {
url: string; url: string;
onUrlChange: (u: string) => void; onUrlChange: (u: string) => void;
urlPlaceholder: string; urlPlaceholder: string;
existingFileName?: string;
onClearExistingFile?: () => void;
} }
export function FileOrUrlField({ export function FileOrUrlField({
label, required, mode, onModeChange, label, required, mode, onModeChange,
accept, file, onFileChange, inputRef, accept, file, onFileChange, inputRef,
url, onUrlChange, urlPlaceholder, url, onUrlChange, urlPlaceholder,
existingFileName, onClearExistingFile,
}: FileOrUrlFieldProps) { }: FileOrUrlFieldProps) {
return ( return (
<div className='space-y-1.5'> <div className='space-y-1.5'>
{/* Label + toggle */} {/* Label + toggle */}
<div className='flex items-center justify-between ml-1'> <div className='flex items-center justify-between ml-1'>
<label className='text-xs font-bold text-neutral-400 uppercase tracking-wider'> <Label>
{label}{required && <span className='text-orange-500 ml-0.5'>*</span>} {label}{required && <span className='text-orange-500 ml-0.5'>*</span>}
</label> </Label>
<div className='flex items-center gap-1 rounded-xl bg-[#181818] border border-neutral-800 p-1 text-xs'> <div className='flex items-center gap-1 rounded-xl bg-[#181818] border border-neutral-850 p-1 text-xs'>
<button <button
type='button' type='button'
onClick={() => onModeChange('file')} onClick={() => onModeChange('file')}
@ -60,7 +65,7 @@ export function FileOrUrlField({
<div <div
onClick={() => inputRef.current?.click()} onClick={() => inputRef.current?.click()}
className={`flex cursor-pointer items-center gap-3 rounded-xl border-2 border-dashed px-4 py-4 transition-all duration-200 ${ className={`flex cursor-pointer items-center gap-3 rounded-xl border-2 border-dashed px-4 py-4 transition-all duration-200 ${
file file || existingFileName
? 'border-orange-500/50 bg-[#161616]' ? 'border-orange-500/50 bg-[#161616]'
: 'border-neutral-800 hover:border-neutral-700 bg-[#161616]/40 hover:bg-[#161616]/70' : 'border-neutral-800 hover:border-neutral-700 bg-[#161616]/40 hover:bg-[#161616]/70'
}`} }`}
@ -97,6 +102,30 @@ export function FileOrUrlField({
<X size={15} /> <X size={15} />
</button> </button>
</> </>
) : existingFileName ? (
<>
<div className='flex flex-1 items-center gap-3.5 min-w-0'>
<div className='w-8 h-8 rounded-lg bg-orange-500/10 border border-orange-500/20 flex items-center justify-center shrink-0'>
<Upload size={14} className='text-orange-500' />
</div>
<div className="flex flex-col min-w-0">
<span className='text-sm text-white font-semibold truncate'>{existingFileName}</span>
<span className='text-[10px] text-neutral-500 font-medium mt-0.5'>
Existing File (Keep)
</span>
</div>
</div>
<button
type='button'
onClick={(e) => {
e.stopPropagation();
if (onClearExistingFile) onClearExistingFile();
}}
className='shrink-0 text-neutral-500 hover:text-white p-1 hover:bg-neutral-800 rounded-lg transition-colors cursor-pointer'
>
<X size={15} />
</button>
</>
) : ( ) : (
<div className='flex flex-1 items-center gap-3 text-neutral-500 py-1'> <div className='flex flex-1 items-center gap-3 text-neutral-500 py-1'>
<Upload size={16} className="text-neutral-600" /> <Upload size={16} className="text-neutral-600" />
@ -107,9 +136,8 @@ export function FileOrUrlField({
)} )}
</div> </div>
) : ( ) : (
<input <Input
type='url' type='url'
className='w-full bg-[#181818] border border-neutral-800 rounded-xl px-4 py-3 text-sm text-white placeholder-neutral-700 focus:border-orange-500 focus:outline-none transition-colors duration-200'
placeholder={urlPlaceholder} placeholder={urlPlaceholder}
value={url} value={url}
onChange={(e) => onUrlChange(e.target.value)} onChange={(e) => onUrlChange(e.target.value)}