112 lines
3.0 KiB
JavaScript
112 lines
3.0 KiB
JavaScript
import { useRef } from "react";
|
|
import Label from "./Label";
|
|
import Tooltip from "./Tooltip";
|
|
import { formatFileSize, getIconByFileType } from "../../utils/appUtils";
|
|
|
|
const SingleFileUploader = ({
|
|
label = "Upload Document",
|
|
required = false,
|
|
value,
|
|
onChange,
|
|
onRemove,
|
|
disabled,
|
|
error,
|
|
accept = ".pdf,.jpg,.jpeg,.png",
|
|
maxSizeMB = 25,
|
|
hint = "(PDF, JPG, PNG, max 5MB)",
|
|
}) => {
|
|
const inputRef = useRef(null);
|
|
|
|
const handleFileSelect = async (e) => {
|
|
const file = e.target.files?.[0];
|
|
if (!file) return;
|
|
|
|
// Validate size
|
|
if (file.size > maxSizeMB * 1024 * 1024) {
|
|
alert(`File size cannot exceed ${maxSizeMB}MB`);
|
|
e.target.value = "";
|
|
return;
|
|
}
|
|
|
|
// Convert to base64
|
|
const base64Data = await new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
reader.onload = () => resolve(reader.result.split(",")[1]);
|
|
reader.onerror = (err) => reject(err);
|
|
});
|
|
|
|
const attachmentObj = {
|
|
fileName: file.name,
|
|
base64Data,
|
|
invoiceAttachmentTypeId: "", // set dynamically if needed
|
|
contentType: file.type,
|
|
fileSize: file.size,
|
|
description: "",
|
|
isActive: true,
|
|
};
|
|
|
|
onChange(attachmentObj);
|
|
e.target.value = "";
|
|
};
|
|
|
|
return (
|
|
<div className="col-md-12">
|
|
<Label className="form-label" required={required}>
|
|
{label}
|
|
</Label>
|
|
|
|
<div
|
|
className="border border-secondary border-dashed rounded p-4 text-center bg-textMuted position-relative"
|
|
style={{ cursor: "pointer" }}
|
|
onClick={() => inputRef.current.click()}
|
|
>
|
|
<i className="bx bx-cloud-upload d-block bx-lg"></i>
|
|
<span className="text-muted d-block">
|
|
Click to select or click here to browse
|
|
</span>
|
|
<small className="text-muted">{hint}</small>
|
|
|
|
<input
|
|
type="file"
|
|
ref={inputRef}
|
|
accept={accept}
|
|
style={{ display: "none" }}
|
|
onChange={handleFileSelect}
|
|
disabled={disabled}
|
|
/>
|
|
</div>
|
|
|
|
{error && <small className="danger-text">{error}</small>}
|
|
|
|
{value && (
|
|
<div className="mt-3">
|
|
<div className="d-flex align-items-center justify-content-between bg-white border rounded p-2">
|
|
<div className="d-flex align-items-center gap-2">
|
|
<i
|
|
className={`bx ${getIconByFileType(value.contentType)} fs-3`}
|
|
></i>
|
|
|
|
<div className="d-flex flex-column text-truncate">
|
|
<span className="fw-semibold small text-truncate">
|
|
{value.fileName}
|
|
</span>
|
|
<small className="text-muted">
|
|
{value.fileSize ? formatFileSize(value.fileSize) : ""}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
|
|
<i
|
|
className="bx bx-trash text-danger fs-5 cursor-pointer"
|
|
onClick={onRemove}
|
|
></i>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SingleFileUploader;
|