From 61d4cf9eda80fce0572374a32d0faeeca52bcdef Mon Sep 17 00:00:00 2001
From: Jose Manuel Serrano Amaut <a20122128@pucp.pe>
Date: Tue, 14 Mar 2023 20:17:20 -0500
Subject: [PATCH] [REF]: Refactor Dropzone, FileInputButton and core upload
 related methods to set the server response inside of ExtFiles

---
 .../dropzone/components/dropzone/Dropzone.tsx |  23 +-
 .../components/dropzone/DropzoneProps.ts      | 232 +++++++++---------
 .../file-input-button/FileInputButton.tsx     | 193 +++++++++------
 .../file-input-button/InputButtonProps.ts     |  66 ++---
 src/files-ui/core/types/ExtFile.ts            |   4 +-
 src/files-ui/core/types/uploadTypes.ts        |   4 +-
 src/files-ui/core/upload/errors.upload.ts     |  15 +-
 src/files-ui/core/upload/response.upload.ts   |  25 +-
 src/files-ui/core/upload/upload.ts            |   2 +-
 src/files-ui/core/upload/utils.upload.ts      |  12 +-
 src/files-ui/core/utils/fakeupload.utils.ts   |  46 +++-
 11 files changed, 357 insertions(+), 265 deletions(-)

diff --git a/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx b/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx
index 7ff135c..733721a 100644
--- a/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx
+++ b/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx
@@ -221,6 +221,7 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => {
     //  setIsUploading(true);
     //PREPARING stage
     console.log("validateFilesFlag", validateFilesFlag);
+    onUploadStart?.(localFiles);
 
     arrOfExtFilesInstances =
       ExtFileManager.setFileListMapPreparing(
@@ -251,8 +252,7 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => {
     console.log("FileManagerLog after sleep", arrOfExtFilesInstances);
 
     //return;
-    let serverResponses: Array<UploadResponse> = [];
-
+    let serverResponses: Array<ExtFile> = [];
     //Uplad files one by one
     for (let i = 0; i < arrOfExtFilesInstances.length; i++) {
       const currentExtFileInstance: ExtFileInstance = arrOfExtFilesInstances[i];
@@ -282,13 +282,14 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => {
 
         //UPLOADING => UPLOAD()
         //upload one file and notify about change
-        let uploadResponse: UploadResponse;
+        let uploadResponse: ExtFile;
 
         if (fakeUpload) {
           uploadResponse = await fakeFuiUpload(
             currentExtFileInstance,
             DropzoneLocalizer
           );
+
           let fakeProgress = 0;
           while (fakeProgress < 100) {
             fakeProgress += getRandomInt(21, 35);
@@ -313,7 +314,7 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => {
           }
         }
 
-        const { uploadedFile } = uploadResponse;
+        const uploadedFile = uploadResponse;
         console.log("fake uploadResponse uploadedFile", uploadedFile);
 
         //update instances
@@ -335,18 +336,22 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => {
 
         console.log(
           "pre sanitizeArrExtFile",
-          arrOfExtFilesInstances.map((F) => {return{status:F.uploadStatus,message:F.uploadMessage}})
+          arrOfExtFilesInstances.map((F) => {
+            return { status: F.uploadStatus, message: F.uploadMessage };
+          })
         );
 
-
         //CHANGE
         if (!(currentExtFileInstance.uploadStatus === "aborted"))
           await sleepTransition();
 
         console.log(
           "pre sanitizeArrExtFile",
-          arrOfExtFilesInstances.map((F) => {return{status:F.uploadStatus,message:F.uploadMessage}})
+          arrOfExtFilesInstances.map((F) => {
+            return { status: F.uploadStatus, message: F.uploadMessage };
+          })
         );
+
         handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true);
 
         if (uploadedFile.uploadStatus === "error") {
@@ -369,7 +374,9 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => {
     setLocalMessage(
       finishUploadMessenger(missingUpload - totalRejected, totalRejected)
     );
-    setIsUploading(false);
+    setTimeout(() => {
+      setIsUploading(false);
+    }, 2000);
   };
 
   const handleAbortUpload = () => {
diff --git a/src/files-ui/components/dropzone/components/dropzone/DropzoneProps.ts b/src/files-ui/components/dropzone/components/dropzone/DropzoneProps.ts
index 22b0c26..5ac50b2 100644
--- a/src/files-ui/components/dropzone/components/dropzone/DropzoneProps.ts
+++ b/src/files-ui/components/dropzone/components/dropzone/DropzoneProps.ts
@@ -31,51 +31,6 @@ export interface DropzoneFullProps extends OverridableComponentProps {
   */
   value?: ExtFile[];
 
-  ///////////////          STYLING          ///////////                             
-  /**
-   * The background color for dropzone container,
-   * @default 'transparent'
-   */
-  backgroundColor?: string;
-  /**
-   * The min height of the container in string format
-   * If the value is given un number format "px" will be assumed
-   * @default "190px"
-   * 
-   * examples: 
-   *    "50vh"
-   *    "20%"
-   *    "40em"
-   *    "1rem"
-   */
-  minHeight?: string | number;
-  ///////////////          DISPLAY SETTINGS          ///////////    
-  /**
-   * If false, hides the dropzone footer
-   * By default is true
-   */
-  footer?: boolean;
-  /**
-   * If false, hides the dropzone header
-   * By default is true
-   */
-  header?: boolean;
-  /**
-  * The label to place when no files were selected
-  */
-  label?: string;
-  /**
-  * The language to be used in Dropzone labels
-  * Currently only English, French , Portuguese, Chinnese (traditional and simplyfied), Russian and Spanish are supported
-  * @default "EN-en"
-  */
-  localization?: Localization;
-  /**
-   * If true, will show a ripple everytime
-   * the user drops files or selects files
-   */
-  disableRipple?: boolean;
-
   ///////////////         VALIDATION STAGE        ///////////////
   /**
    * The max file size allowed in bytes
@@ -127,37 +82,6 @@ export interface DropzoneFullProps extends OverridableComponentProps {
    * This flag will only work if validation is active.
    */
   cleanFiles?: boolean;
-  ///////// INPUT
-  /** 
-  * If true, the dropzone component itself will be clickable
-  * If false, the file dialog will not be opened
-  * @default true
-  */
-  clickable?: boolean;
-  /**
-   * The behaviour when new files are selected or dropped
-   * @default 'add'
-   */
-  behaviour?: 'add' | 'replace';
-  /**
-  * If `true`, the component is disabled.
-  * @default false
-  */
-  disabled?: boolean;
-  /**
-   * If `true`, the drop operation will be performed in a layer that covers the complete component container.
-   * @default true
-   */
-  dropOnLayer?: boolean;
-  /**
-   * Method for performing specific tasks on drag enter operations
-   */
-  onDragEnter?: (evt: React.DragEvent<HTMLDivElement>) => void;
-  /**
-   * Method for performing specific tasks on drag leave operations
-   */
-  onDragLeave?: (evt: React.DragEvent<HTMLDivElement>) => void;
-
 
   ///////////////         UPLOAD STAGE        ///////////////
   /**
@@ -180,16 +104,66 @@ export interface DropzoneFullProps extends OverridableComponentProps {
    * Unlike Onchange, onUploadStart will only return a list of files thta are candidates to be uploaded,
    * in case they are valid or upload status is "error"
    */
-  onUploadStart?: (files: ExtFile[]) => void;
+  onUploadStart?: (extFiles: ExtFile[]) => void;
   /**
   * This event returns as first aparameter the list of responses for each file following the structure:
   * responses = [
   *  {id: <the file id>, serverResponse: the server response}
   * ]
   */
-  onUploadFinish?: (responses: UploadResponse[]) => void;
+  onUploadFinish?: (extFiles: ExtFile[]) => void;
+
+
+  ///////////////          STYLING          ///////////                             
+  /**
+   * The background color for dropzone container,
+   * @default 'transparent'
+   */
+  backgroundColor?: string;
+  /**
+   * The min height of the container in string format
+   * If the value is given un number format "px" will be assumed
+   * @default "190px"
+   * 
+   * examples: 
+   *    "50vh"
+   *    "20%"
+   *    "40em"
+   *    "1rem"
+   */
+  minHeight?: string | number;
+  // LABEL   
+  /**
+  * The label to place when no files were selected
+  */
+  label?: string;
+
+  //LOCALIZATION
+  /**
+  * The language to be used in Dropzone labels
+  * @default "EN-en"
+  */
+  localization?: Localization;
+
+  //RIPPLE
+  /**
+   * If true, will not show a ripple effect everytime
+   * the user drops files or clicks the dropzone for selecting files
+   * @default false
+   */
+  disableRipple?: boolean;
 
 
+  /**
+   * Method for performing specific tasks on drag enter operations
+   */
+  onDragEnter?: (evt: React.DragEvent<HTMLDivElement>) => void;
+  /**
+   * Method for performing specific tasks on drag leave operations
+   */
+  onDragLeave?: (evt: React.DragEvent<HTMLDivElement>) => void;
+
+  //ACTION BUTTONS
   /**
    * The configuration needed for uploading the files.
    * When "uploadConfig" is not given or uploadConfig.url is undefined
@@ -198,15 +172,58 @@ export interface DropzoneFullProps extends OverridableComponentProps {
    */
   actionButtons?: DropzoneActions;
 
-  //advancedConfig?: DropzoneAdvancedConfig;
+  ///////// DROP LAYER
+  /**
+   * If `true`, the drop operation will be performed in a layer that covers the complete component container.
+   * @default true
+   */
+  dropOnLayer?: boolean;
 
+  // HEADER AND FOOTER
+  /**
+   * If false, hides the dropzone footer
+   * @default true
+   */
+  footer?: boolean;
+  /**
+   * If false, hides the dropzone header
+   * @default true
+   */
+  header?: boolean;
+  /**
+   * Configuration related to the dropzone header
+   */
   headerConfig?: HeaderConfig;
-
+  /**
+   * Configuration  related to the dropzone footer
+   */
   footerConfg?: FooterConfig;
+
+  //DISABLED
+  /**
+  * If `true`, the component is disabled.
+  * @default false
+  */
+  disabled?: boolean;
+  //CLICKABLE
+  /** 
+    * If true, the dropzone component itself will be clickable
+    * If false, the file dialog will not be opened
+    * @default true
+    */
+  clickable?: boolean;
+
+  // ADD OR REPLACE
+  /**
+   * The behaviour when new files are selected or dropped
+   * @default 'add'
+   */
+  behaviour?: 'add' | 'replace';
 }
 
 
-export type HeaderItems = {
+export type HeaderConfig = {
+  customHeader?: JSX.Element;
   deleteFiles?: boolean;
   cleanFiles?: boolean;
   uploadFiles?: boolean;
@@ -214,19 +231,8 @@ export type HeaderItems = {
   maxFileSize?: boolean;
   validFilesCount?: boolean;
 }
-export interface HeaderConfigMap extends OverridableComponentProps {
-  customHeader?: JSX.Element;
-}
-export type HeaderConfig =
-  {
-    [P in keyof HeaderConfigMap]: HeaderConfigMap[P]
-  } & {
-    [H in keyof HeaderItems]: HeaderItems[H]
-  }
-export interface FooterConfigMap extends OverridableComponentProps {
-  customFooter?: JSX.Element;
-}
-export type FooterItems = {
+
+export type FooterConfig = {
   /**
    * Allowed types: .png,image/*
    */
@@ -243,15 +249,9 @@ export type FooterItems = {
    * 
    */
   noMissingFilesLabel?: boolean;
+  customFooter?: JSX.Element;
 }
 
-export type FooterConfig =
-  {
-    [P in keyof FooterConfigMap]: FooterConfigMap[P]
-  } & {
-    [H in keyof FooterItems]: FooterItems[H]
-  }
-
 
 export type DropzoneActionButton = {
   children?: React.ReactNode;
@@ -273,19 +273,6 @@ export interface DropzoneActions {
 }
 
 
-export interface AdvancedConfigItem {
-  style?: React.CSSProperties;
-  className?: string;
-}
-
-export type DropzoneAdvancedConfig = {
-  dropzoneLayer: any;
-  dropzoneContainer: any;
-  dropzoneLabel: any;
-}
-
-
-
 type DefDivProps = React.HTMLProps<HTMLDivElement>;
 type DivPropsOmitDropzoneFullProps = Omit<DefDivProps, keyof DropzoneFullProps>;
 
@@ -307,4 +294,19 @@ export const defaultDrozoneProps: DropzoneProps =
   header: true,
   footer: true,
   value: [],
-}
\ No newline at end of file
+}
+
+
+
+/* 
+export interface AdvancedConfigItem {
+  style?: React.CSSProperties;
+  className?: string;
+}
+
+export type DropzoneAdvancedConfig = {
+  dropzoneLayer: any;
+  dropzoneContainer: any;
+  dropzoneLabel: any;
+}
+ */
\ No newline at end of file
diff --git a/src/files-ui/components/file-input-button/FileInputButton.tsx b/src/files-ui/components/file-input-button/FileInputButton.tsx
index efc624b..8f3b1d1 100644
--- a/src/files-ui/components/file-input-button/FileInputButton.tsx
+++ b/src/files-ui/components/file-input-button/FileInputButton.tsx
@@ -9,14 +9,18 @@ import {
   fakeFuiUpload,
   fileListToExtFileArray,
   FileValidatorProps,
+  getRandomInt,
   //FunctionLabel,
   handleClickInput,
   instantPreparingToUploadOne,
+  isUploadAbleExtFile,
   isValidateActive,
   LocalLabels,
+  sanitizeArrExtFile,
   sleepPreparing,
   sleepTransition,
   toUploadableExtFileList,
+  unexpectedErrorUploadResult,
   UploadConfig,
   uploadExtFile,
   UploadResponse,
@@ -134,36 +138,48 @@ const FileInputButton: React.FC<FileInputButtonProps> = (
    * @returns nothing
    */
   const uploadfiles = async (localFiles: ExtFile[]): Promise<void> => {
-    if (!url) return;
-
+    //set uploading flag to true
     setIsUploading(true);
-    print_manager(localFiles, "start");
-    // set flag to true
-    // recieve on the new list
-    // initialize new list of ExtFileInstances
-    let arrOfExtFilesInstances: ExtFileInstance[] = [];
+
     //avoid to call upload if not allowed
-    if (isUploading || localFiles.length === 0 || !arrOfExtFilesInstances) {
+    // flag is already true or there isnt files
+    //url was not provided
+    if (isUploading || localFiles.length === 0 || !url) {
+      setIsUploading(false);
       return;
     }
+    // initialize a new list of ExtFileInstances
+    let arrOfExtFilesInstances: ExtFileInstance[] = [];
 
     const totalNumber: number = localFiles.length;
-    const missingUpload: number = localFiles.filter((x: ExtFile) => {
-      return (
-        (!validateFilesFlag || (validateFilesFlag && x.valid)) &&
-        x.uploadStatus !== "success"
-      );
-    }).length;
+    console.log("upload start: totalNumber", totalNumber);
+
+    const missingUpload: number = localFiles.filter((extFile: ExtFile) =>
+      isUploadAbleExtFile(extFile, validateFilesFlag)
+    ).length;
+
+    console.log("upload start: missingUpload", missingUpload);
 
     let totalRejected: number = 0;
     let currentCountUpload: number = 0;
 
+    /* const uploadingMessenger: FunctionLabel =
+      DropzoneLocalizer.uploadingMessage as FunctionLabel; */
+
+    //no missing to upload
+    if (!(missingUpload > 0)) {
+      console.log("upload start: noFilesMessage", missingUpload);
+
+      //setLocalMessage(DropzoneLocalizer.noFilesMessage as string);
+      setIsUploading(false);
+      return;
+    }
+
+    //setLocalMessage(uploadingMessenger(`${missingUpload}/${totalNumber}`));
     //  setIsUploading(true);
     //PREPARING stage
-    //use methods to update on static class
-    //obtain a fresher dui file list
-    console.log("***** before setFileListMapPreparing");
-    console.table(localFiles);
+    console.log("validateFilesFlag", validateFilesFlag);
+
     arrOfExtFilesInstances =
       ExtFileManager.setFileListMapPreparing(
         inputButtonId,
@@ -172,8 +188,6 @@ const FileInputButton: React.FC<FileInputButtonProps> = (
         cleanOnUpload as boolean
       ) || [];
 
-    console.log("***** FileManagerLog  setFileListMapPreparing");
-    console.table(arrOfExtFilesInstances);
     const newExtFileLocal: ExtFile[] = [...arrOfExtFilesInstances].map((x) =>
       x.toExtFile()
     );
@@ -191,100 +205,135 @@ const FileInputButton: React.FC<FileInputButtonProps> = (
     //AWAIT when preparing time is given
     //general sleep for all files
     await sleepPreparing(preparingTime);
-    // workaround for preventing getting the uploadStatus as undefined
-    /*  arrOfExtFilesInstances.forEach((F) => {
-    F.uploadStatus = "preparing";
-      }); */
-    //variable for storing responses
-    //console.log("uploadfiles after sleep response",response);
+
     console.log("FileManagerLog after sleep", arrOfExtFilesInstances);
 
     //return;
-    let serverResponses: Array<UploadResponse> = [];
+    let serverResponses: Array<ExtFile> = [];
 
     for (let i = 0; i < arrOfExtFilesInstances.length; i++) {
       const currentExtFileInstance: ExtFileInstance = arrOfExtFilesInstances[i];
+
       console.log(
         "FileManagerLog currentExtFileInstance " + i,
         currentExtFileInstance
       );
 
-      if (currentExtFileInstance.uploadStatus === "preparing") {
+      if (
+        currentExtFileInstance.uploadStatus === "preparing" &&
+        !currentExtFileInstance.extraData?.deleted
+      ) {
         //set stage to "uploading" in one file and notify change
         // PREPARING => UPLOADING
+        await sleepTransition();
+
         instantPreparingToUploadOne(currentExtFileInstance);
-        /*   setLocalMessage(
-          uploadingMessenger(`${++currentCountUpload}/${missingUpload}`)
-        ); */
-        //CHANGE
-        handleFilesChange([...arrOfExtFilesInstances], true);
+
+        //messge in footer
+        /* setLocalMessage(
+        uploadingMessenger(`${++currentCountUpload}/${missingUpload}`)
+      ); */
+
+        //CHANGE FILES
+        handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true);
 
         //UPLOADING => UPLOAD()
         //upload one file and notify about change
-        let uploadResponse: UploadResponse;
-        try {
-          uploadResponse = fakeUpload
-            ? await fakeFuiUpload(currentExtFileInstance, DropzoneLocalizer)
-            : await uploadExtFile(
-                currentExtFileInstance,
-                url,
-                method,
-                headers,
-                uploadLabel
-              );
-        } catch (error) {
-          uploadResponse = {
-            id: currentExtFileInstance.id,
-            serverResponse: {
-              success: false,
-              message: "Error on upload: unexpected error " + error,
-              payload: {},
-            },
-            uploadedFile: { ...currentExtFileInstance },
-          };
+        let uploadResponse: ExtFile;
+
+        if (fakeUpload) {
+          uploadResponse = await fakeFuiUpload(
+            currentExtFileInstance,
+            DropzoneLocalizer
+          );
+
+          let fakeProgress = 0;
+          while (fakeProgress < 100) {
+            fakeProgress += getRandomInt(21, 35);
+            currentExtFileInstance.progress =
+              fakeProgress > 100 ? 100 : fakeProgress;
+            await sleepTransition(1000);
+            handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true);
+          }
+        } else {
+          try {
+            uploadResponse = await uploadExtFile(
+              currentExtFileInstance,
+              url,
+              method,
+              headers,
+              uploadLabel
+            );
+          } catch (error) {
+            uploadResponse = unexpectedErrorUploadResult(
+              currentExtFileInstance.toExtFile()
+            );
+          }
         }
 
-        const { uploadedFile } = uploadResponse;
+        const uploadedFile = uploadResponse;
+        console.log("fake uploadResponse uploadedFile", uploadedFile);
+
         //update instances
         currentExtFileInstance.uploadStatus = uploadedFile.uploadStatus;
         currentExtFileInstance.uploadMessage = uploadedFile.uploadMessage;
 
-        //add fake progress only on fakeupload
-        if (fakeUpload) {
-          console.log(
-            "Adding fake progress",
-            fakeUpload,
-            uploadedFile.progress
-          );
-          currentExtFileInstance.progress = uploadedFile.progress;
-        }
+        console.log(
+          "fake uploadResponse currentExtFileInstance",
+          currentExtFileInstance
+        );
+        console.log(
+          "fake uploadResponse currentExtFileInstance",
+          currentExtFileInstance.uploadStatus
+        );
+        console.log(
+          "fake uploadResponse currentExtFileInstance",
+          currentExtFileInstance.uploadMessage
+        );
+
+        console.log(
+          "pre sanitizeArrExtFile",
+          arrOfExtFilesInstances.map((F) => {
+            return { status: F.uploadStatus, message: F.uploadMessage };
+          })
+        );
+
         //CHANGE
         if (!(currentExtFileInstance.uploadStatus === "aborted"))
           await sleepTransition();
 
-        handleFilesChange(
-          arrOfExtFilesInstances.map((x: ExtFileInstance) => x.toExtFile()),
-          true
+        console.log(
+          "pre sanitizeArrExtFile",
+          arrOfExtFilesInstances.map((F) => {
+            return { status: F.uploadStatus, message: F.uploadMessage };
+          })
         );
 
+        handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true);
+
         if (uploadedFile.uploadStatus === "error") {
           totalRejected++;
         }
 
         serverResponses.push(uploadResponse);
+      } else {
+        handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true);
       }
     }
 
+    handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true);
+
     // upload group finished :D
     onUploadFinish?.(serverResponses);
-
-    /* const finishUploadMessenger: FunctionLabel =
+    /* 
+    const finishUploadMessenger: FunctionLabel =
       DropzoneLocalizer.uploadFinished as FunctionLabel;
-
-   setLocalMessage(
+    setLocalMessage(
       finishUploadMessenger(missingUpload - totalRejected, totalRejected)
     ); */
-    setIsUploading(false);
+    setTimeout(() => {
+      setIsUploading(false);
+    }, 2000);
   };
   /**
    * Performs the changes in the FuiFile list.
diff --git a/src/files-ui/components/file-input-button/InputButtonProps.ts b/src/files-ui/components/file-input-button/InputButtonProps.ts
index fd729e5..fe4b1e7 100644
--- a/src/files-ui/components/file-input-button/InputButtonProps.ts
+++ b/src/files-ui/components/file-input-button/InputButtonProps.ts
@@ -44,8 +44,8 @@ interface InputButtonFullProps {
 
     ///////////////         VALIDATION STAGE        ///////////////
     /**
-     * The max file size allowed in bytes
-     */
+   * The max file size allowed in bytes
+   */
     maxFileSize?: number;
     /**
      * The max number of files to be accepted.
@@ -74,24 +74,21 @@ interface InputButtonFullProps {
      * ```
      */
     validator?: (f: File) => CustomValidateFileResponse;
+
     /**
- * Flag that indicates that dropzone will automatically remove non valid files.
- * This will happen every time user drops files or selects files from file dialog.
- * This flag will only work if validation is active.
- */
+     * Flag that indicates that dropzone will automatically remove non valid files.
+     * This will happen every time user drops files or selects files from file dialog.
+     * This flag will only work if validation is active.
+     */
     autoClean?: boolean;
-    /**
-   * The behaviour when new files are selected or dropped
-   * @default 'add'
-   */
-    behaviour?: 'add' | 'replace';
+
     ///////////////         UPLOAD STAGE        ///////////////
     /**
-    * The configuration needed for uploading the files.
-    * When "uploadConfig" is not given or uploadConfig.url is undefined
-    * the upload button will not be visible
-    * and uploadOnDrop flag will not work
-    */
+  * The configuration needed for uploading the files.
+  * When "uploadConfig" is not given or uploadConfig.url is undefined
+  * the upload button will not be visible
+  * and uploadOnDrop flag will not work
+  */
     uploadConfig?: UploadConfig;
     /**
      * Flag that indicates Dropzone to perform a fake upload process.
@@ -101,29 +98,34 @@ interface InputButtonFullProps {
      */
     fakeUpload?: boolean;
     /**
- * This event is triggered when upload process starts
- * also returns the list of files that will be uploaded,
- * Unlike Onchange, onUploadStart will only return a list of files thta are candidates to be uploaded,
- * in case they are valid or upload status is "error"
- */
-    onUploadStart?: (files: ExtFile[]) => void;
+     * This event is triggered when upload process starts
+     * also returns the list of files that will be uploaded,
+     * Unlike Onchange, onUploadStart will only return a list of files thta are candidates to be uploaded,
+     * in case they are valid or upload status is "error"
+     */
+    onUploadStart?: (extFiles: ExtFile[]) => void;
     /**
     * This event returns as first aparameter the list of responses for each file following the structure:
     * responses = [
     *  {id: <the file id>, serverResponse: the server response}
     * ]
     */
-    onUploadFinish?: (responses: UploadResponse[]) => void;
-
-
+    onUploadFinish?: (extFiles: ExtFile[]) => void;
 
-    /**
-     * The configuration needed for uploading the files.
-     * When "uploadConfig" is not given or uploadConfig.url is undefined
-     * the upload button will not be visible
-     * and uploadOnDrop flag will not work
-     */
-    actionButtons?: DropzoneActions;
+  //ACTION BUTTONS
+  /**
+   * The configuration needed for uploading the files.
+   * When "uploadConfig" is not given or uploadConfig.url is undefined
+   * the upload button will not be visible
+   * and uploadOnDrop flag will not work
+   */
+  actionButtons?: DropzoneActions;
+  // ADD OR REPLACE
+  /**
+   * The behaviour when new files are selected or dropped
+   * @default 'add'
+   */
+  behaviour?: 'add' | 'replace';
 }
 
 type MaterialButtonPropsOmitInputButtonFullProps = Omit<MaterialButtonProps, keyof InputButtonFullProps>;
diff --git a/src/files-ui/core/types/ExtFile.ts b/src/files-ui/core/types/ExtFile.ts
index 2e840ac..73a4cf4 100644
--- a/src/files-ui/core/types/ExtFile.ts
+++ b/src/files-ui/core/types/ExtFile.ts
@@ -29,7 +29,9 @@ export declare type ExtFile = {
      * The size of the file. Used mostly for displaying file data from server.
      */
     size?: number;
-
+    /**
+     * Link, URI or string representation of an image
+     */
     imageUrl?: string;
     /**
      * a flag that determines whether the file is valid, not valid or it is not validated.
diff --git a/src/files-ui/core/types/uploadTypes.ts b/src/files-ui/core/types/uploadTypes.ts
index 82a709d..4c5f4b2 100644
--- a/src/files-ui/core/types/uploadTypes.ts
+++ b/src/files-ui/core/types/uploadTypes.ts
@@ -18,7 +18,7 @@ export declare type UploadResponse = {
 }
 export type ServerResponse = {
     success: boolean;
-    message: string;
-    payload: any;
+    message?: string;
+    payload?: any;
 }
 
diff --git a/src/files-ui/core/upload/errors.upload.ts b/src/files-ui/core/upload/errors.upload.ts
index d4b3b00..fa7da54 100644
--- a/src/files-ui/core/upload/errors.upload.ts
+++ b/src/files-ui/core/upload/errors.upload.ts
@@ -22,16 +22,13 @@ export const UNEXPECTED_ERROR_RESPONSE = {
     payload: {}
 }
 
-export const NO_XHR_PROVIDED_ERROR = (extFile: ExtFile): UploadResponse => {
+export const NO_XHR_PROVIDED_ERROR = (extFile: ExtFile): ExtFile => {
     return {
-        uploadedFile:
-        {
-            ...extFile,
-            uploadMessage: "Unable to upload. xhr object was not provided",
-            uploadStatus: "error"
-        },
 
-        id: extFile.id,
-        serverResponse: {}
+        ...extFile,
+        uploadMessage: "Unable to upload. xhr object was not provided",
+        uploadStatus: "error",
+
+        serverResponse: { success: false, }
     }
 }
\ No newline at end of file
diff --git a/src/files-ui/core/upload/response.upload.ts b/src/files-ui/core/upload/response.upload.ts
index 4d7eee8..32707c4 100644
--- a/src/files-ui/core/upload/response.upload.ts
+++ b/src/files-ui/core/upload/response.upload.ts
@@ -29,16 +29,13 @@ export const JsonParseResponse = (xhr: XMLHttpRequest): ServerResponse => {
 export const makeSuccessUploadResponse = (
     extFile: ExtFile,
     responseFui: ServerResponse
-): UploadResponse => {
+): ExtFile => {
     return {
-        id: extFile.id,
+        ...extFile,
         serverResponse: responseFui,
-        uploadedFile:
-        {
-            ...extFile,
-            uploadMessage: responseFui.message,
-            uploadStatus: "success"
-        },
+        uploadMessage: responseFui.message,
+        uploadStatus: "success"
+
     }
 }
 
@@ -46,17 +43,13 @@ export const makeSuccessUploadResponse = (
 export const makeErrorUploadResponse = (
     extFile: ExtFile,
     responseFui: ServerResponse
-): UploadResponse => {
+): ExtFile => {
     console.log("makeErrorUploadResponse", extFile, responseFui);
     return {
-        id: extFile.id,
+        ...extFile,
+        uploadMessage: responseFui.message,
+        uploadStatus: "error", 
         serverResponse: responseFui,
-        uploadedFile:
-        {
-            ...extFile,
-            uploadMessage: responseFui.message,
-            uploadStatus: "error"
-        },
     }
 }
 
diff --git a/src/files-ui/core/upload/upload.ts b/src/files-ui/core/upload/upload.ts
index 236c2fb..1062703 100644
--- a/src/files-ui/core/upload/upload.ts
+++ b/src/files-ui/core/upload/upload.ts
@@ -100,7 +100,7 @@ export const uploadExtFile = async (
     method?: Method,
     headers?: Record<string, string>,
     uploadLabel?: string,
-): Promise<UploadResponse> => {
+): Promise<ExtFile> => {
     return new Promise(async (resolve, reject) => {
         try {
             const uploader: XMLHttpRequest | undefined = extFile.xhr;
diff --git a/src/files-ui/core/upload/utils.upload.ts b/src/files-ui/core/upload/utils.upload.ts
index 316ab7f..727caa9 100644
--- a/src/files-ui/core/upload/utils.upload.ts
+++ b/src/files-ui/core/upload/utils.upload.ts
@@ -1,15 +1,11 @@
 
 import { ExtFile, ExtFileInstance, ServerResponse, UploadResponse, UPLOADSTATUS } from "../types"
 
-export const unexpectedErrorUploadResult = (extFile: ExtFile): UploadResponse => {
+export const unexpectedErrorUploadResult = (extFile: ExtFile): ExtFile => {
     return {
-        id: extFile.id,
-        uploadedFile:
-        {
-            ...extFile,
-            uploadMessage: "Unexpected error",
-            uploadStatus: "error"
-        },
+        ...extFile,
+        uploadMessage: "Unexpected error",
+        uploadStatus: "error",
         serverResponse: {
             success: false,
             message: "Error on upload: unexpected error ",
diff --git a/src/files-ui/core/utils/fakeupload.utils.ts b/src/files-ui/core/utils/fakeupload.utils.ts
index 54d20f3..1346884 100644
--- a/src/files-ui/core/utils/fakeupload.utils.ts
+++ b/src/files-ui/core/utils/fakeupload.utils.ts
@@ -108,7 +108,51 @@ export const uploadOneExtFile = (
 export const fakeFuiUpload = (
     extFileInstance: ExtFileInstance,
     DropzoneLocalizer = DropzoneLocalizerSelector("EN-en")
+): Promise<ExtFile> => {
+    
+    const extFile:ExtFile = extFileInstance.toExtFile();
+    
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            const randomNumber: number = Math.floor(Math.random() * 10);
+            if (randomNumber % 2 === 0) {
+                const success = true;
+                const message = DropzoneLocalizer.fakeuploadsuccess as string;
+                const payload = { url: "" };
+                resolve({
+                    ...extFile,
+                    serverResponse: { success, message, payload },
+                    uploadStatus: "success",
+                    uploadMessage: message,
+
+                });
+            } else {
+                const success = false;
+                const message = DropzoneLocalizer.fakeUploadError as string;
+                const payload = {};
+                resolve({
+                    ...extFile,
+                    serverResponse: { success, message, payload },
+                    uploadStatus: "error",
+                    uploadMessage: message,
+                });
+            }
+        }, 1700);
+    });
+};
+
+
+/**
+ * 
+ * @param extFile the extFile to upload 
+ * @param DropzoneLocalizer the localization
+ * @returns a duiUploadResponse object that describes the result
+ */
+export const fakeFuiUploadExtFile = (
+    extFileInstance: ExtFileInstance,
+    DropzoneLocalizer = DropzoneLocalizerSelector("EN-en")
 ): Promise<UploadResponse> => {
+    
     const extFile:ExtFile = extFileInstance.toExtFile();
     
     return new Promise((resolve, reject) => {
@@ -118,6 +162,7 @@ export const fakeFuiUpload = (
                 const status = true;
                 const message = DropzoneLocalizer.fakeuploadsuccess as string;
                 const payload = { url: "" };
+
                 resolve({
                     id: extFile.id,
                     serverResponse: { status, message, payload },
@@ -147,4 +192,3 @@ export const fakeFuiUpload = (
         }, 1700);
     });
 };
-
-- 
GitLab