From 08938ad7f0fc24f60015e3fe4077d3a3b6e1da74 Mon Sep 17 00:00:00 2001 From: AmirReza Jamali Date: Tue, 7 Jul 2026 10:54:51 +0330 Subject: [PATCH] Fix company document removal in edit mode --- .../forms/companies/CreateCompany.tsx | 2 + .../forms/companies/companyFormSections.tsx | 11 +++- .../companies/documents/CompanyDocuments.tsx | 38 ++++++++--- .../documents/useCompanyDocumentsPresenter.ts | 17 ++--- .../companies/useCreateCompanyPresenter.ts | 63 +++++++++++++------ src/hooks/queries/useCompaniesQueries.ts | 24 ++++++- 6 files changed, 116 insertions(+), 39 deletions(-) diff --git a/src/components/forms/companies/CreateCompany.tsx b/src/components/forms/companies/CreateCompany.tsx index 5a90a81..82d272e 100644 --- a/src/components/forms/companies/CreateCompany.tsx +++ b/src/components/forms/companies/CreateCompany.tsx @@ -21,6 +21,7 @@ const CreateCompany = ({ defaultValues, editMode, id }: IProps) => { control, watch, setValue, + persistDocumentFileIds, companyCategories, } = useCreateCompanyPresenter({ editMode, defaultValues, id }); @@ -38,6 +39,7 @@ const CreateCompany = ({ defaultValues, editMode, id }: IProps) => { categoryItems, watch, setValue, + persistDocumentFileIds, })} register={register} control={control} diff --git a/src/components/forms/companies/companyFormSections.tsx b/src/components/forms/companies/companyFormSections.tsx index d7db6ce..4b14d41 100644 --- a/src/components/forms/companies/companyFormSections.tsx +++ b/src/components/forms/companies/companyFormSections.tsx @@ -25,6 +25,7 @@ interface CompanyFormSectionsOptions { categoryItems: ILabelValue[]; watch: UseFormWatch; setValue: UseFormSetValue; + persistDocumentFileIds?: (ids: string[]) => Promise; } const optionalNumber = (value: unknown) => { @@ -240,6 +241,7 @@ export const createCompanyFormSections = ({ categoryItems, watch, setValue, + persistDocumentFileIds, }: CompanyFormSectionsOptions): DynamicFormSection[] => [ { id: companyInformationSectionId, @@ -652,7 +654,14 @@ export const createCompanyFormSections = ({ setValue("document_file_ids", ids)} + onChange={(ids) => + setValue("document_file_ids", ids, { + shouldDirty: true, + shouldTouch: true, + shouldValidate: true, + }) + } + onRemovePersist={persistDocumentFileIds} /> ), }, diff --git a/src/components/forms/companies/documents/CompanyDocuments.tsx b/src/components/forms/companies/documents/CompanyDocuments.tsx index 670b52b..060e1d6 100644 --- a/src/components/forms/companies/documents/CompanyDocuments.tsx +++ b/src/components/forms/companies/documents/CompanyDocuments.tsx @@ -8,9 +8,15 @@ interface IProps { companyId?: string; value: string[]; onChange: (ids: string[]) => void; + onRemovePersist?: (ids: string[]) => Promise; } -const CompanyDocuments = ({ companyId, value, onChange }: IProps) => { +const CompanyDocuments = ({ + companyId, + value, + onChange, + onRemovePersist, +}: IProps) => { const { isUploading, isBusy, @@ -20,13 +26,18 @@ const CompanyDocuments = ({ companyId, value, onChange }: IProps) => { errorMessage, onSelectFiles, onRemove, - } = useCompanyDocumentsPresenter({ companyId, value, onChange }); + } = useCompanyDocumentsPresenter({ + companyId, + value, + onChange, + onRemovePersist, + }); const inputId = "company-documents-input"; return (
-
+
{ : "cursor-pointer hover:-translate-y-0.5 hover:border-primary hover:bg-primary/[0.03] dark:hover:bg-primary/[0.08]" }`} > -
- +
+ {
) : removingFileId ? (
-

Removing document…

+

+ Removing document… +

@@ -115,8 +133,12 @@ const CompanyDocuments = ({ companyId, value, onChange }: IProps) => {

    {failed.map((item, index) => ( -
  • - {item.filename}: {item.error} +
  • + {item.filename}:{" "} + {item.error}
  • ))}
diff --git a/src/components/forms/companies/documents/useCompanyDocumentsPresenter.ts b/src/components/forms/companies/documents/useCompanyDocumentsPresenter.ts index 39fe349..e93787d 100644 --- a/src/components/forms/companies/documents/useCompanyDocumentsPresenter.ts +++ b/src/components/forms/companies/documents/useCompanyDocumentsPresenter.ts @@ -1,10 +1,7 @@ "use client"; import { useUploadCompanyDocuments } from "@/hooks/queries"; import { fileService, FailedFile } from "@/lib/api"; -import { - MAX_DOCUMENT_FILES, - validateDocumentFile, -} from "@/utils/shared"; +import { MAX_DOCUMENT_FILES, validateDocumentFile } from "@/utils/shared"; import { ChangeEvent, useState } from "react"; interface IProps { @@ -14,6 +11,8 @@ interface IProps { value: string[]; /** Persists the updated document file id list back to the form. */ onChange: (ids: string[]) => void; + /** Persists document removal for an existing company. */ + onRemovePersist?: (ids: string[]) => Promise; } /** @@ -28,6 +27,7 @@ const useCompanyDocumentsPresenter = ({ companyId, value, onChange, + onRemovePersist, }: IProps) => { const [isUploading, setIsUploading] = useState(false); const [removingFileId, setRemovingFileId] = useState(null); @@ -106,15 +106,18 @@ const useCompanyDocumentsPresenter = ({ setErrorMessage(undefined); try { + const nextValue = value.filter((id) => id !== fileId); + // In create mode the file is an orphan in storage (not linked to any // company), so clean it up before dropping it from the form. In edit mode - // the removal is persisted by the form's update (PUT replaces the full - // document list). + // persist the replacement document list immediately. if (!companyId) { await fileService.deleteFile(fileId); + } else { + await onRemovePersist?.(nextValue); } - onChange(value.filter((id) => id !== fileId)); + onChange(nextValue); } catch (error) { console.error("ERROR caught while deleting company document:", error); setErrorMessage("Failed to remove document. Please try again."); diff --git a/src/components/forms/companies/useCreateCompanyPresenter.ts b/src/components/forms/companies/useCreateCompanyPresenter.ts index 0353e89..c6bdad7 100644 --- a/src/components/forms/companies/useCreateCompanyPresenter.ts +++ b/src/components/forms/companies/useCreateCompanyPresenter.ts @@ -49,6 +49,13 @@ const useCreateCompanyPresenter = ({ defaultValues, editMode, id }: IProps) => { const { mutate: updateCompany, isPending: isUpdating } = useUpdateCompany( id as string, ); + const { mutateAsync: updateCompanyDocuments } = useUpdateCompany( + id as string, + { + redirectOnSuccess: false, + showSuccessToast: false, + }, + ); const optionalNumber = (value: unknown) => { if (value === "" || value === null || value === undefined) return undefined; @@ -56,27 +63,30 @@ const useCreateCompanyPresenter = ({ defaultValues, editMode, id }: IProps) => { return Number(value); }; + const formatCompanyData = ( + data: ICreateCompanyCredentials, + documentFileIds = data.document_file_ids, + ) => ({ + ...data, + founded_year: optionalNumber(data.founded_year), + employee_count: optionalNumber(data.employee_count), + annual_revenue: optionalNumber(data.annual_revenue), + document_file_ids: documentFileIds ?? [], + tags: { + keywords: data?.tags?.keywords?.length ? data?.tags?.keywords : [], + categories: data?.tags?.categories?.length ? data?.tags?.categories : [], + cpv_codes: data?.tags?.cpv_codes?.length ? data?.tags?.cpv_codes : [], + specializations: data?.tags?.specializations?.length + ? data?.tags?.specializations + : [], + certifications: data?.tags?.certifications?.length + ? data?.tags?.certifications + : [], + }, + }); + const onSubmit: SubmitHandler = (data) => { - const formattedData = { - ...data, - founded_year: optionalNumber(data.founded_year), - employee_count: optionalNumber(data.employee_count), - annual_revenue: optionalNumber(data.annual_revenue), - document_file_ids: data.document_file_ids ?? [], - tags: { - keywords: data?.tags?.keywords?.length ? data?.tags?.keywords : [], - categories: data?.tags?.categories?.length - ? data?.tags?.categories - : [], - cpv_codes: data?.tags?.cpv_codes?.length ? data?.tags?.cpv_codes : [], - specializations: data?.tags?.specializations?.length - ? data?.tags?.specializations - : [], - certifications: data?.tags?.certifications?.length - ? data?.tags?.certifications - : [], - }, - }; + const formattedData = formatCompanyData(data); if (editMode) { updateCompany({ id: id as string, @@ -87,6 +97,18 @@ const useCreateCompanyPresenter = ({ defaultValues, editMode, id }: IProps) => { } }; + const persistDocumentFileIds = async (documentFileIds: string[]) => { + if (!editMode || !id) return; + + await updateCompanyDocuments({ + id, + credentials: formatCompanyData( + watch() as ICreateCompanyCredentials, + documentFileIds, + ) as ICreateCompanyCredentials, + }); + }; + const handleCancel = () => { reset(); router.back(); @@ -104,6 +126,7 @@ const useCreateCompanyPresenter = ({ defaultValues, editMode, id }: IProps) => { router, watch, setValue, + persistDocumentFileIds, companyCategories, }; }; diff --git a/src/hooks/queries/useCompaniesQueries.ts b/src/hooks/queries/useCompaniesQueries.ts index 5ab1706..48a90b5 100644 --- a/src/hooks/queries/useCompaniesQueries.ts +++ b/src/hooks/queries/useCompaniesQueries.ts @@ -31,7 +31,18 @@ export const useCreateCompany = () => { }); }; -export const useUpdateCompany = (id: string) => { +interface UseUpdateCompanyOptions { + redirectOnSuccess?: boolean; + showSuccessToast?: boolean; +} + +export const useUpdateCompany = ( + id: string, + { + redirectOnSuccess = true, + showSuccessToast = true, + }: UseUpdateCompanyOptions = {}, +) => { const queryClient = useQueryClient(); const router = useRouter(); const mutationKey = useMemo( @@ -42,14 +53,21 @@ export const useUpdateCompany = (id: string) => { mutationKey, mutationFn: companiesService.updateCompany, onSuccess: () => { - toast.success("Company updated successfully"); + if (showSuccessToast) { + toast.success("Company updated successfully"); + } queryClient.invalidateQueries({ queryKey: [API_ENDPOINTS.COMPANIES.READ_ALL], }); queryClient.invalidateQueries({ queryKey: [API_ENDPOINTS.COMPANIES.READ_ALL, "details"], }); - router.push("/companies"); + queryClient.invalidateQueries({ + queryKey: [API_ENDPOINTS.COMPANIES.READ_ALL, id, "details"], + }); + if (redirectOnSuccess) { + router.push("/companies"); + } }, }); };