Files
tm_panel/src/components/forms/companies/documents/CompanyDocuments.tsx
T
AmirReza Jamali 08938ad7f0
continuous-integration/drone/push Build is passing
Fix company document removal in edit mode
2026-07-07 10:54:51 +03:30

152 lines
5.2 KiB
TypeScript

"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;
onRemovePersist?: (ids: string[]) => Promise<void>;
}
const CompanyDocuments = ({
companyId,
value,
onChange,
onRemovePersist,
}: IProps) => {
const {
isUploading,
isBusy,
removingFileId,
uploadProgress,
failed,
errorMessage,
onSelectFiles,
onRemove,
} = useCompanyDocumentsPresenter({
companyId,
value,
onChange,
onRemovePersist,
});
const inputId = "company-documents-input";
return (
<div className="flex flex-col gap-4">
<div className="shadow-theme-xs relative overflow-hidden rounded-2xl border border-stroke/70 bg-gradient-to-br from-white via-white to-primary/[0.04] p-4 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={isBusy}
/>
<label
htmlFor={inputId}
aria-disabled={isBusy}
className={`group flex 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 dark:border-dark-3 dark:bg-dark/20 ${
isBusy
? "cursor-not-allowed opacity-60"
: "cursor-pointer hover:-translate-y-0.5 hover:border-primary hover:bg-primary/[0.03] dark:hover:bg-primary/[0.08]"
}`}
>
<div className="shadow-theme-xs mb-3 rounded-2xl bg-primary/[0.12] p-3 text-primary 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>
) : removingFileId ? (
<div className="mt-3">
<p className="text-xs font-medium text-primary">
Removing document
</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 w-full animate-pulse rounded-full bg-gradient-to-r from-primary to-purple-500" />
</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={isBusy}
isRemoving={removingFileId === fileId}
/>
))}
</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;