metms_fe/src/components/utils/UploadMultipleFile/UploadMultipleFile.tsx
2026-06-06 01:02:07 +07:00

212 lines
5.6 KiB
TypeScript

"use client";
import React from "react";
import Image from "next/image";
import {
X,
CirclePlus,
FileText,
FileSpreadsheet,
FileType,
FileImage,
} from "lucide-react";
import clsx from "clsx";
import { PropsUploadMultipleFile, UploadFileItem } from "./interface";
export default function UploadMultipleFile({
images = [],
setImages,
isDisableDelete = false,
}: PropsUploadMultipleFile) {
/* =========================
CHECK IMAGE FILE
========================= */
const isImageFile = (item: UploadFileItem) => {
const type = item?.fileType || "";
return (
type.startsWith("image/") ||
/\.(jpg|jpeg|png|gif|webp)$/i.test(item?.path || "")
);
};
/* =========================
GET FILE ICON
========================= */
const renderFileIcon = (item: UploadFileItem) => {
const fileName = item?.fileName || item?.path?.split("/").pop() || "";
const ext = fileName.split(".").pop()?.toLowerCase();
switch (ext) {
case "jpg":
case "jpeg":
case "png":
case "gif":
case "webp":
return <FileImage size={28} />;
case "pdf":
return <FileText size={28} />;
case "xls":
case "xlsx":
return <FileSpreadsheet size={28} />;
case "doc":
case "docx":
return <FileType size={28} />;
case "ppt":
case "pptx":
return <FileType size={28} />;
case "txt":
return <FileText size={28} />;
default:
return <FileText size={28} />;
}
};
/* =========================
UPLOAD FILES
========================= */
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (!files) return;
const newFiles: UploadFileItem[] = Array.from(files).map((file) => ({
file,
url: URL.createObjectURL(file),
fileName: file.name,
fileType: file.type,
}));
setImages((prev) => [...prev, ...newFiles]);
};
/* =========================
DELETE FILE
========================= */
const handleDelete = (index: number) => {
setImages((prev) => {
const target = prev[index];
if (target?.url) {
URL.revokeObjectURL(target.url);
}
return prev.filter((_, i) => i !== index);
});
};
return (
<div className="flex flex-wrap items-center gap-2">
{/* FILE LIST */}
{images?.length > 0 && (
<div className="flex flex-wrap items-center gap-2">
{images.map((item, index) => {
const fileUrl =
item?.url ||
(item?.path
? `${process.env.NEXT_PUBLIC_IMAGE}${item.path}`
: "");
return (
<div
key={index}
className="relative h-[72px] w-[72px] overflow-hidden rounded-md border border-[#99a2b34d]"
>
{/* IMAGE */}
{isImageFile(item) ? (
<a
href={fileUrl}
target="_blank"
rel="noreferrer"
className="block h-full w-full"
>
<Image
src={fileUrl}
alt="file"
fill
className="object-cover"
/>
</a>
) : (
<a
href={`${process.env.NEXT_PUBLIC_IMAGE}/${fileUrl}`}
target="_blank"
rel="noreferrer"
className="flex h-full w-full flex-col items-center justify-center gap-1 bg-gray-50 p-1"
>
{renderFileIcon(item)}
<span className="line-clamp-2 text-center text-[9px]">
{item?.fileName || item?.path?.split("/").pop()}
</span>
</a>
)}
{/* DELETE */}
{isDisableDelete && !item?.file && item?.img ? null : (
<button
type="button"
onClick={() => handleDelete(index)}
className="absolute right-[2px] top-[2px] flex h-[18px] w-[18px] items-center justify-center rounded-sm bg-white transition active:scale-90"
>
<X size={14} color="#8496AC" />
</button>
)}
</div>
);
})}
</div>
)}
{/* UPLOAD BUTTON */}
<div className="flex items-center gap-2">
<label
className={clsx(
"flex h-[72px] w-[72px] cursor-pointer items-center justify-center rounded-md border bg-white",
"border-[#99a2b34d] transition hover:border-gray-400",
)}
>
<CirclePlus color="rgba(198, 201, 206, 1)" />
<input
hidden
multiple
type="file"
accept="
image/*,
.pdf,
.doc,
.docx,
.xls,
.xlsx,
.ppt,
.pptx,
.txt
"
onClick={(e) => {
(e.target as HTMLInputElement).value = "";
}}
onChange={handleFileChange}
/>
</label>
<div className="flex flex-col">
<p className="text-[14px] font-medium text-[#2f3643]">Upload file</p>
<p className="text-[12px] font-medium text-[#99a2b3]">
JPG, PNG, PDF, Word, Excel, PPT ( 50MB)
</p>
</div>
</div>
</div>
);
}