fix:input file validator

This commit is contained in:
Rama Priyanto 2025-06-11 12:26:16 +07:00
parent f8829985f6
commit 5418d97ad5
2 changed files with 110 additions and 109 deletions

View File

@ -11,118 +11,121 @@ interface FileWithPreview extends File {
} }
interface FileUploaderProps { interface FileUploaderProps {
onDrop: (files: FileWithPreview[]) => void; onDrop: (files: FileWithPreview[]) => void;
accept: Accept; accept: Accept;
maxSize: number; maxSize: number;
label: string; label: string;
className?: string; className?: string;
isMultiple?: boolean; isMultiple?: boolean;
} }
const FileUploader = ({ onDrop, accept, maxSize, label, className = "", isMultiple = true }: FileUploaderProps) => { const FileUploader = ({
const [files, setFiles] = useState<FileWithPreview[]>([]); onDrop,
accept,
maxSize,
label,
className = "",
isMultiple = true,
}: FileUploaderProps) => {
const [files, setFiles] = useState<FileWithPreview[]>([]);
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
accept, accept: accept,
maxSize: maxSize * 1024 * 1024, maxSize: maxSize * 1024 * 1024,
onDrop: (acceptedFiles) => { onDrop: (acceptedFiles) => {
const mappedFiles = acceptedFiles.map((file) => Object.assign(file)); const mappedFiles = acceptedFiles.map((file) => Object.assign(file));
setFiles((prevFiles) => [...prevFiles, ...mappedFiles]); setFiles((prevFiles) => [...prevFiles, ...mappedFiles]);
onDrop(mappedFiles); onDrop(mappedFiles);
}, },
}); });
const renderFilePreview = (file: FileWithPreview) => { const renderFilePreview = (file: FileWithPreview) => {
if (file.type.startsWith("image")) { if (file.type.startsWith("image")) {
return ( return (
<Image <Image
width={48} width={48}
height={48} height={48}
alt={file.name} alt={file.name}
src={URL.createObjectURL(file)} src={URL.createObjectURL(file)}
className=" rounded border p-0.5" className=" rounded border p-0.5"
/> />
); );
} else { } else {
return <Icon icon="tabler:file-description" />; return <Icon icon="tabler:file-description" />;
} }
}; };
const handleRemoveFile = (file: FileWithPreview) => {
const uploadedFiles = files;
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
setFiles([...filtered]);
};
const handleRemoveAllFiles = () => { const handleRemoveFile = (file: FileWithPreview) => {
setFiles([]); const uploadedFiles = files;
onDrop([]); const filtered = uploadedFiles.filter((i) => i.name !== file.name);
}; setFiles([...filtered]);
};
const fileList = files.map((file) => (
<div const handleRemoveAllFiles = () => {
key={file.name} setFiles([]);
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md" onDrop([]);
> };
<div className="flex gap-3 items-center">
<div className="file-preview">{renderFilePreview(file)}</div> const fileList = files.map((file) => (
<div> <div
<div className=" text-sm text-card-foreground">{file.name}</div> key={file.name}
<div className=" text-xs font-light text-muted-foreground"> className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
{Math.round(file.size / 100) / 10 > 1000 ? ( >
<>{(Math.round(file.size / 100) / 10000).toFixed(1)}</> <div className="flex gap-3 items-center">
) : ( <div className="file-preview">{renderFilePreview(file)}</div>
<>{(Math.round(file.size / 100) / 10).toFixed(1)}</> <div>
)} <div className=" text-sm text-card-foreground">{file.name}</div>
{" kb"} <div className=" text-xs font-light text-muted-foreground">
</div> {Math.round(file.size / 100) / 10 > 1000 ? (
<>{(Math.round(file.size / 100) / 10000).toFixed(1)}</>
) : (
<>{(Math.round(file.size / 100) / 10).toFixed(1)}</>
)}
{" kb"}
</div> </div>
</div> </div>
<Button
size="icon"
color="destructive"
variant="outline"
className=" border-none rounded-full"
onClick={() => handleRemoveFile(file)}
>
<Icon icon="tabler:x" className=" h-5 w-5" />
</Button>
</div> </div>
));
<Button
return ( size="icon"
color="destructive"
variant="outline"
className=" border-none rounded-full"
onClick={() => handleRemoveFile(file)}
>
<Icon icon="tabler:x" className=" h-5 w-5" />
</Button>
</div>
));
return (
<Fragment>
<div {...getRootProps({ className: "dropzone" })} className={className}>
<input {...getInputProps()} />
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
<CloudUpload className="text-default-300 w-10 h-10" />
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
{/* Drop files here or click to upload. */}
Tarik file disini atau klik untuk upload.
</h4>
<div className=" text-xs text-muted-foreground">
( {label}
Ukuran maksimal {maxSize} MB.)
</div>
</div>
</div>
{files.length ? (
<Fragment> <Fragment>
<div {...getRootProps({ className: "dropzone" })} className={className}> <div>{fileList}</div>
<input {...getInputProps()} /> <div className=" flex justify-between gap-2">
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col"> <Button color="destructive" onClick={handleRemoveAllFiles}>
<CloudUpload className="text-default-300 w-10 h-10" /> Remove All
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80"> </Button>
{/* Drop files here or click to upload. */} </div>
Tarik file disini atau klik untuk upload.
</h4>
<div className=" text-xs text-muted-foreground">
( {label}
Ukuran maksimal {maxSize} MB.)
</div>
</div>
</div>
{files.length ? (
<Fragment>
<div>{fileList}</div>
<div className=" flex justify-between gap-2">
<Button
color="destructive"
onClick={handleRemoveAllFiles}
>
Remove All
</Button>
</div>
</Fragment>
) : null}
</Fragment> </Fragment>
); ) : null}
} </Fragment>
);
};
export default FileUploader;
export default FileUploader;

View File

@ -805,8 +805,7 @@ export default function FormTask() {
<Label>{t("audio-visual")}</Label> <Label>{t("audio-visual")}</Label>
<FileUploader <FileUploader
accept={{ accept={{
"mp4/*": [], "video/*": [],
"mov/*": [],
}} }}
maxSize={100} maxSize={100}
label="Upload file dengan format .mp4 atau .mov." label="Upload file dengan format .mp4 atau .mov."
@ -828,7 +827,7 @@ export default function FormTask() {
<Label>{t("text")}</Label> <Label>{t("text")}</Label>
<FileUploader <FileUploader
accept={{ accept={{
"pdf/*": [], "application/pdf": [],
}} }}
maxSize={100} maxSize={100}
label="Upload file dengan format .pdf." label="Upload file dengan format .pdf."
@ -848,8 +847,7 @@ export default function FormTask() {
/> />
<FileUploader <FileUploader
accept={{ accept={{
"mp3/*": [], "audio/*": [],
"wav/*": [],
}} }}
maxSize={100} maxSize={100}
label="Upload file dengan format .mp3 atau .wav." label="Upload file dengan format .mp3 atau .wav."