feat(documents): implement document upload functionality for companies
- Added DocumentItem component to display uploaded documents in CompanyDetailsPage. - Introduced useUploadCompanyDocuments hook for handling document uploads. - Updated CreateCompany form to include document upload capabilities and validation for file types and sizes. - Enhanced API services to support batch document uploads and retrieval of document information. - Improved response handling in the file service for better error management and user feedback.
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"use client";
|
||||
import { DOCUMENT_FILE_ACCEPT, MAX_DOCUMENT_FILES } from "@/utils/shared";
|
||||
import DocumentItem from "./DocumentItem";
|
||||
import useCompanyDocumentsPresenter from "./useCompanyDocumentsPresenter";
|
||||
|
||||
interface IProps {
|
||||
/** Present in edit mode; switches uploads to the company-documents endpoint. */
|
||||
companyId?: string;
|
||||
value: string[];
|
||||
onChange: (ids: string[]) => void;
|
||||
}
|
||||
|
||||
const CompanyDocuments = ({ companyId, value, onChange }: IProps) => {
|
||||
const {
|
||||
isUploading,
|
||||
uploadProgress,
|
||||
failed,
|
||||
errorMessage,
|
||||
onSelectFiles,
|
||||
onRemove,
|
||||
} = useCompanyDocumentsPresenter({ companyId, value, onChange });
|
||||
|
||||
const inputId = "company-documents-input";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="relative overflow-hidden rounded-2xl border border-stroke/70 bg-gradient-to-br from-white via-white to-primary/[0.04] p-4 shadow-theme-xs dark:border-dark-3 dark:from-dark-2 dark:via-dark-2 dark:to-primary/[0.12]">
|
||||
<input
|
||||
id={inputId}
|
||||
type="file"
|
||||
multiple
|
||||
accept={DOCUMENT_FILE_ACCEPT}
|
||||
className="sr-only"
|
||||
onChange={onSelectFiles}
|
||||
disabled={isUploading}
|
||||
/>
|
||||
<label
|
||||
htmlFor={inputId}
|
||||
className="group flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed border-stroke/80 bg-white/70 px-4 py-9 text-center transition-all duration-300 hover:-translate-y-0.5 hover:border-primary hover:bg-primary/[0.03] dark:border-dark-3 dark:bg-dark/20 dark:hover:bg-primary/[0.08]"
|
||||
>
|
||||
<div className="mb-3 rounded-2xl bg-primary/[0.12] p-3 text-primary shadow-theme-xs transition-transform duration-300 group-hover:scale-105">
|
||||
<svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p className="text-sm font-semibold text-dark dark:text-white">
|
||||
Click to upload documents
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-dark-5">
|
||||
PDF, Word, Excel, images, archives — up to 100MB each
|
||||
</p>
|
||||
<div className="mt-4 inline-flex items-center rounded-full border border-primary/20 bg-primary/[0.08] px-3 py-1 text-[11px] font-medium text-primary dark:border-primary/40 dark:bg-primary/[0.18]">
|
||||
Max {MAX_DOCUMENT_FILES} files per upload
|
||||
</div>
|
||||
</label>
|
||||
|
||||
{isUploading ? (
|
||||
<div className="mt-3">
|
||||
<p className="text-xs font-medium text-primary">
|
||||
Uploading documents… {companyId ? "" : `${uploadProgress}%`}
|
||||
</p>
|
||||
<div className="mt-1.5 h-1.5 w-full overflow-hidden rounded-full bg-gray-2 dark:bg-dark-3">
|
||||
<div
|
||||
className="h-full rounded-full bg-gradient-to-r from-primary to-purple-500 transition-all duration-300"
|
||||
style={{ width: companyId ? "100%" : `${uploadProgress}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{errorMessage ? (
|
||||
<p className="text-sm font-medium text-red-500">{errorMessage}</p>
|
||||
) : null}
|
||||
|
||||
{value.length > 0 ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
{value.map((fileId) => (
|
||||
<DocumentItem
|
||||
key={fileId}
|
||||
fileId={fileId}
|
||||
onRemove={onRemove}
|
||||
disabled={isUploading}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-dark-5">No documents attached yet.</p>
|
||||
)}
|
||||
|
||||
{failed.length > 0 ? (
|
||||
<div className="rounded-xl border border-error/35 bg-error/5 p-3">
|
||||
<p className="mb-2 text-xs font-semibold uppercase tracking-wide text-error">
|
||||
Failed uploads
|
||||
</p>
|
||||
<ul className="space-y-1">
|
||||
{failed.map((item, index) => (
|
||||
<li key={`${item.filename}-${index}`} className="text-sm text-error">
|
||||
<span className="font-medium">{item.filename}</span>: {item.error}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CompanyDocuments;
|
||||
Reference in New Issue
Block a user