siakad-itm/resources/js/components/file-upload-field.tsx

116 lines
3.8 KiB
TypeScript

import { FileIcon, RotateCcw, UploadIcon, XIcon } from 'lucide-react';
import { useRef, useState } from 'react';
import InputError from '@/components/input-error';
import {
Attachment,
AttachmentAction,
AttachmentActions,
AttachmentContent,
AttachmentDescription,
AttachmentMedia,
AttachmentTitle,
AttachmentTrigger,
} from '@/components/ui/attachment';
import { Label } from '@/components/ui/label';
type FileUploadFieldProps = {
label?: string;
/** Form field name for the native file input. */
name?: string;
existingFileName?: string | null;
existingFileUrl?: string | null;
error?: string;
accept?: string;
helpText?: string;
};
export function FileUploadField({
label = 'File',
name = 'file',
existingFileName,
existingFileUrl,
error,
accept,
helpText = 'PDF, Word, PPT, Excel, gambar, video, atau zip (maks 10MB)',
}: FileUploadFieldProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [selectedFile, setSelectedFile] = useState<File | null>(null);
function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
setSelectedFile(e.target.files?.[0] ?? null);
}
function handleReset() {
setSelectedFile(null);
if (inputRef.current) {
inputRef.current.value = '';
}
}
const hasNewFile = selectedFile !== null;
const hasExistingFile = !hasNewFile && !!existingFileName;
const displayName = selectedFile?.name ?? existingFileName ?? null;
const displayUrl = hasNewFile ? null : existingFileUrl;
const state = error ? 'error' : displayName ? 'done' : 'idle';
return (
<div className="grid gap-2">
<Label>{label}</Label>
<input
ref={inputRef}
type="file"
name={name}
accept={accept}
onChange={handleFileChange}
className="hidden"
/>
<Attachment state={state}>
<AttachmentTrigger onClick={() => inputRef.current?.click()} />
<AttachmentMedia>
<FileIcon />
</AttachmentMedia>
<AttachmentContent>
<AttachmentTitle>
{displayName ?? 'Klik untuk pilih file'}
</AttachmentTitle>
<AttachmentDescription>
{displayName
? hasExistingFile
? 'File saat ini — klik untuk mengganti'
: 'Klik untuk mengganti file'
: helpText}
</AttachmentDescription>
</AttachmentContent>
<AttachmentActions>
{displayUrl && (
<AttachmentAction asChild>
<a
href={displayUrl}
target="_blank"
rel="noreferrer"
onClick={(e) => e.stopPropagation()}
>
<UploadIcon className="rotate-180" />
</a>
</AttachmentAction>
)}
{hasNewFile && (
<AttachmentAction
onClick={(e) => {
e.stopPropagation();
handleReset();
}}
>
{hasExistingFile ? <RotateCcw /> : <XIcon />}
</AttachmentAction>
)}
</AttachmentActions>
</Attachment>
<InputError message={error} />
</div>
);
}