You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by nm...@apache.org on 2021/07/02 15:28:20 UTC

[ofbiz-framework] branch trunk updated (2cf1e9d -> 7120cf7)

This is an automated email from the ASF dual-hosted git repository.

nmalin pushed a change to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git.


    from 2cf1e9d  Improved: Remove SVN references in build (OFBIZ-12267) (#305)
     new 2a509be  Improved: Improve js function OfbizUtil.ajaxSubmitFormUpdateAreas (OFBIZ-12268)
     new 7120cf7  Implemented: Keep paginate list query on dynamic refresh (OFBIZ-12261)

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/apache/ofbiz/widget/WidgetWorker.java |  12 ++
 .../org/apache/ofbiz/widget/model/ModelForm.java   |   9 +-
 .../widget/renderer/macro/MacroCommonRenderer.java |  26 ++--
 .../widget/renderer/macro/MacroMenuRenderer.java   |   2 +-
 .../widget/renderer/macro/MacroScreenRenderer.java |   2 +-
 .../template/macro/HtmlFormMacroLibrary.ftl        |  10 ++
 .../webapp/common/js/util/OfbizUtil.js             | 159 ++++++++++++++++-----
 themes/common-theme/widget/CommonForms.xml         |  11 ++
 8 files changed, 176 insertions(+), 55 deletions(-)

[ofbiz-framework] 02/02: Implemented: Keep paginate list query on dynamic refresh (OFBIZ-12261)

Posted by nm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nmalin pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit 7120cf7d5f264830385105dea233c7f56998242e
Author: Nicolas Malin <ni...@nereide.fr>
AuthorDate: Fri Jul 2 17:27:21 2021 +0200

    Implemented: Keep paginate list query on dynamic refresh (OFBIZ-12261)
    
    When you display a list with search criteria, pagination information and/or sort parameters, if you update an element of this list with a update area, you loose all of them.
    
    Example:
    
    <form name="ListProductMeters" type="list" list-name="productMeters" target="updateProductMeter" paginate-target="ListProductMeters">
        <actions>
           <entity-condition entity-name="ProductMeter">...
        </actions>
        <auto-fields-service service-name="updateProductMeter"/>
        <field name="meterName"><text size="20"/></field>
        <field name="updateButton"><submit/></field>
        <on-event-update-area event-type="paginate"/>
        <on-event-update-area event-type="sort-column"/>
        <on-event-update-area event-type="submit"/>
    </form>
    
    Or this works well if you move an next page sort the list.
    I realize the same principle for each event which do a modification of element in the list and after refresh it.
    
    I also introduce the possibility to submit a form on modal (for edition) when you press 'enter' that will refresh the origin list.
    
    Last point, the queryString element use to propage pagination state is also forwarded on callback element to keep this on update the list.
    
    Thanks to Gil Portenseigne is help.
---
 .../java/org/apache/ofbiz/widget/WidgetWorker.java | 12 ++++++++++
 .../org/apache/ofbiz/widget/model/ModelForm.java   |  9 ++++++--
 .../widget/renderer/macro/MacroCommonRenderer.java | 26 +++++++++++-----------
 .../widget/renderer/macro/MacroMenuRenderer.java   |  2 +-
 .../widget/renderer/macro/MacroScreenRenderer.java |  2 +-
 .../template/macro/HtmlFormMacroLibrary.ftl        | 10 +++++++++
 themes/common-theme/widget/CommonForms.xml         | 11 +++++++++
 7 files changed, 55 insertions(+), 17 deletions(-)

diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/WidgetWorker.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/WidgetWorker.java
index 0fde73c..af34210 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/WidgetWorker.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/WidgetWorker.java
@@ -312,4 +312,16 @@ public final class WidgetWorker {
     public static Delegator getDelegator(Map<String, Object> context) {
         return (Delegator) context.get("delegator");
     }
+
+    /**
+     * Analyse the context to found the _QBESTRING_ parameter and return it as Map
+     * @param context
+     * @return
+     */
+    public static Map<String, Object> resolveParametersMapFromQueryString(Map<String, Object> context) {
+        String qbeString = (String) context.get("_QBESTRING_");
+        return qbeString != null
+                ? UtilHttp.getQueryStringOnlyParameterMap(qbeString.replaceAll("&amp;", "&"))
+                : null;
+    }
 }
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelForm.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelForm.java
index 75b20cc..1121fb8 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelForm.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelForm.java
@@ -2285,7 +2285,6 @@ public abstract class ModelForm extends ModelWidget {
      * elements used in form widgets.
      */
     public static class UpdateArea {
-        public static final List<String> EVENT_TYPES_FOR_LIST_REFRESH = UtilMisc.toList("paginate", "sort-column");
         private final String eventType;
         private final String areaId;
         private final String areaTarget;
@@ -2479,8 +2478,14 @@ public abstract class ModelForm extends ModelWidget {
                     "areaId", WidgetWorker.getScreenStack(context).resolveScreenAreaId(getAreaId()),
                     "areaTarget", getAreaTarget());
 
+            // Propagate on the callback parameters use by pagination list
+            Map<String, Object> parameters = WidgetWorker.resolveParametersMapFromQueryString(context);
+            if (parameters == null) {
+                parameters = new HashMap<>();
+            }
+            parameters.putAll(getParameterMap(context));
+
             JSONConverters.MapToJSON converter = new JSONConverters.MapToJSON();
-            Map<String, Object> parameters = UtilGenerics.cast(getParameterMap(context));
             try {
                 claims.put("parameters", converter.convert(parameters).toString());
             } catch (ConversionException e) {
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
index b2bb62c..d68f401 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
@@ -61,9 +61,7 @@ public class MacroCommonRenderer {
             ModelForm.UpdateArea updateArea = updateAreaIter.next();
 
             //For each update area we need to resolve three information below
-            String areaIdToUpdate;
-            String targetToCall;
-            String parametersToForward;
+            String areaIdToUpdate, targetToCall, parametersToForward;
 
             // 1. areaId to update, use the screen stack with the potential area given by the update area element
             areaIdToUpdate = WidgetWorker.getScreenStack(ctx).resolveScreenAreaId(updateArea.getAreaId());
@@ -73,7 +71,7 @@ public class MacroCommonRenderer {
             String ajaxTarget = updateArea.getAreaTarget(context);
             if (UtilValidate.isEmpty(ajaxTarget)
                     && parentModelForm != null) {
-                if (ModelForm.UpdateArea.EVENT_TYPES_FOR_LIST_REFRESH.contains(updateArea.getEventType())) {
+                if (UtilMisc.toList("multi", "list").contains(parentModelForm.getType())) {
                     ajaxTarget = parentModelForm.getPaginateTarget(ctx);
                 } else {
                     ajaxTarget = parentModelForm.getTarget(ctx, parentModelForm.getTargetType());
@@ -84,14 +82,12 @@ public class MacroCommonRenderer {
             // 3. Build parameters to forward
             String queryString = UtilHttp.getQueryStringFromTarget(ajaxTarget).replace("?", "");
             Map<String, Object> parameters = UtilHttp.getQueryStringOnlyParameterMap(queryString);
+            if (extraParams != null) {
+                parameters.putAll(UtilGenerics.cast(extraParams));
+            }
             parameters.putAll(UtilGenerics.cast(updateArea.getParameterMap(ctx)));
             UtilHttp.canonicalizeParameterMap(parameters);
             parametersToForward = UtilHttp.urlEncodeArgs(parameters, false);
-            if (UtilValidate.isNotEmpty(parametersToForward)
-                    && UtilValidate.isNotEmpty(extraParams)) {
-                parametersToForward += "&";
-            }
-            parametersToForward += buildParamStringFromMap(extraParams);
 
             // 4. build the final string
             sb.append(areaIdToUpdate).append(",")
@@ -118,15 +114,17 @@ public class MacroCommonRenderer {
 
     /**
      * Analyze the context against the link type to resolve the url to call
+     * @param link generic link object
+     * @param linkType link type is resolved from execution context and not directly on the link object
      * @param context
      * @return
      */
-    public static String getLinkUrl(CommonWidgetModels.Link link, Map<String, Object> context) {
+    public static String getLinkUrl(CommonWidgetModels.Link link, String linkType, Map<String, Object> context) {
         String linkUrl = "";
 
         HttpServletRequest request = (HttpServletRequest) context.get("request");
         HttpServletResponse response = (HttpServletResponse) context.get("response");
-        switch (link.getLinkType()) {
+        switch (linkType) {
         case "update-area":
             ModelForm.UpdateArea resolveUpdateArea = new ModelForm.UpdateArea("onclick",
                     WidgetWorker.getScreenStack(context).resolveScreenAreaId(link.getTargetWindow(context)),
@@ -136,9 +134,11 @@ public class MacroCommonRenderer {
             break;
         case "hidden-form":
             if (link.getCallback() != null) {
-                // we assume that the post request
+                // we assume that the request post
                 // wait an immediate response, so we execute the callback directly
-                linkUrl = createAjaxParamsFromUpdateAreas(UtilMisc.toList(link.getCallback()), null, null, null, context);
+                linkUrl = createAjaxParamsFromUpdateAreas(UtilMisc.toList(link.getCallback()),
+                        WidgetWorker.resolveParametersMapFromQueryString(context),
+                        null, null, context);
             }
             break;
         default:
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroMenuRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroMenuRenderer.java
index 418c9aa..d8d0838 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroMenuRenderer.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroMenuRenderer.java
@@ -263,7 +263,7 @@ public class MacroMenuRenderer implements MenuStringRenderer {
         if (targetParameters.length() == 0) {
             targetParameters.append("\"\"");
         }
-        parameters.put("linkUrl", MacroCommonRenderer.getLinkUrl(link.getLink(), context));
+        parameters.put("linkUrl", MacroCommonRenderer.getLinkUrl(link.getLink(), linkType, context));
         parameters.put("actionUrl", actionUrl);
         parameters.put("parameterList", targetParameters);
         String imgStr = "";
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroScreenRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroScreenRenderer.java
index f0f91b1..d8843fc 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroScreenRenderer.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroScreenRenderer.java
@@ -310,7 +310,7 @@ public class MacroScreenRenderer implements ScreenStringRenderer {
         String style = link.getStyle(context);
         String name = link.getName(context);
         String text = link.getText(context);
-        String linkUrl = MacroCommonRenderer.getLinkUrl(link.getLink(), context);
+        String linkUrl = MacroCommonRenderer.getLinkUrl(link.getLink(), linkType, context);
         String imgStr = "";
         ModelScreenWidget.ScreenImage img = link.getImage();
         if (img != null) {
diff --git a/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl b/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
index b6b4391..4652786 100644
--- a/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
@@ -233,6 +233,16 @@ under the License.
       <#else><#if confirmation?has_content> onclick="return confirm('${confirmation?js_string}');"</#if>
     <#if tabindex?has_content> tabindex="${tabindex}"</#if><#rt/>
     </#if>/>
+    <#if containerId?has_content>
+    <#-- the form will be submit by ajax, we inform that perss enter need to call  -->
+    <script>
+        $("form[name='${formName}']").keypress(function(e) {
+          if (e.which === 13 && ! $(e.target).is('textarea')) {
+            e.preventDefault();
+            $("#${id!}").click();
+          }});
+    </script>
+  </#if>
   </#if>
 </#macro>
 
diff --git a/themes/common-theme/widget/CommonForms.xml b/themes/common-theme/widget/CommonForms.xml
index 40aace9..b6bb24a 100644
--- a/themes/common-theme/widget/CommonForms.xml
+++ b/themes/common-theme/widget/CommonForms.xml
@@ -25,10 +25,21 @@ under the License.
     <grid name="CommonSimpleGrid" default-table-style="basic-table dark-grid"/>
     <grid name="CommonBasicGrid"  default-table-style="basic-table hover-bar"
           odd-row-style="alternate-row" header-row-style="header-row-2"/>
+    <grid name="CommonDynamicGrid"  default-table-style="basic-table hover-bar"
+          odd-row-style="alternate-row" header-row-style="header-row-2">
+        <on-event-update-area event-type="sort-column"/>
+        <on-event-update-area event-type="paginate"/>
+    </grid>
 
     <form name="CommonSimpleList" type="list" default-table-style="basic-table dark-grid"/>
     <form name="CommonBasicList"  type="list" default-table-style="basic-table hover-bar"
           odd-row-style="alternate-row" header-row-style="header-row-2"/>
+    <form name="CommonDynamicList" type="list"
+        odd-row-style="alternate-row" default-table-style="basic-table hover-bar" header-row-style="header-row-2">
+        <on-event-update-area event-type="sort-column"/>
+        <on-event-update-area event-type="paginate"/>
+        <on-event-update-area event-type="submit"/>
+    </form>
     <form name="CommonInLineEditList" type="list" default-table-style="basic-table hover-bar"
           odd-row-style="alternate-row" header-row-style="header-row-2" separate-columns="true"/>
 

[ofbiz-framework] 01/02: Improved: Improve js function OfbizUtil.ajaxSubmitFormUpdateAreas (OFBIZ-12268)

Posted by nm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nmalin pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit 2a509be3a9353aed580c21ab3fe84460ac7a7d9f
Author: Nicolas Malin <ni...@nereide.fr>
AuthorDate: Fri Jul 2 17:14:37 2021 +0200

    Improved: Improve js function OfbizUtil.ajaxSubmitFormUpdateAreas (OFBIZ-12268)
    
    At this time the function ajaxSubmitFormUpdateAreas present on themes/common-theme/webapp/common/js/util/OfbizUtil.js take a form, submit it thought ajax and wait a success to analyse a json result for update an area with the cvsAreaString given on parameter.
    
    I realized different improvements :
    
     * refactoring some js code to centralize the analyze of json result for check if the call is a success or error
     * Analyze the content-type of the response if is a json continue as before, else take the return and update first area (useful for submit search or filtering form)
     * Manage the case who the ajax call failed and we inform the user
     * Add a trigger to force the modal close on submit success
    
    Thanks to Leila Mekika, Marie-Aline Pantais and Gil Portenseigne for looking on that.
---
 .../webapp/common/js/util/OfbizUtil.js             | 159 ++++++++++++++++-----
 1 file changed, 121 insertions(+), 38 deletions(-)

diff --git a/themes/common-theme/webapp/common/js/util/OfbizUtil.js b/themes/common-theme/webapp/common/js/util/OfbizUtil.js
index 7412474..b1dcb8b 100644
--- a/themes/common-theme/webapp/common/js/util/OfbizUtil.js
+++ b/themes/common-theme/webapp/common/js/util/OfbizUtil.js
@@ -197,6 +197,9 @@ function bindObservers(bind_element) {
             }
         });
         dialogContainer.dialog("open");
+        dialogContainer.on("closeCurrentModalAfterAjaxSubmitFormUpdateAreasInSuccess", function() {
+            dialogContainer.dialog("destroy");
+        });
     });
     jQuery(bind_element).on("click", "[data-confirm-message]", function(e){
         var element = jQuery(this);
@@ -605,21 +608,30 @@ function ajaxUpdateArea(areaId, target, targetParams) {
         window.location.assign(targetUrl);
         return;
     }
-    waitSpinnerShow();
+    waitSpinnerShow(areaId);
     setTimeout(function() {
         jQuery.ajax({
             url: target,
             type: "POST",
             data: targetParams,
             success: function(data) {
-                jQuery("#" + areaId).html(data);
-                waitSpinnerHide();
+                updateArea(areaId, data)
+                waitSpinnerHide(areaId);
             },
-            error: function(data) {waitSpinnerHide()}
+        error: function(data) { waitSpinnerHide(areaId) }
         });
     }, 0);
 }
 
+function updateArea(areaId, data) {
+    // If the area is indicate as embedded why replace the area instead inject into
+    if (/^embedded/.test(areaId)) {
+        jQuery("#" + areaId).replaceWith(data);
+    } else {
+        jQuery("#" + areaId).html(data);
+    }
+}
+
 /** Update multiple areas (HTML container elements).
   * @param areaCsvString The area CSV string. The CSV string is a flat array in the
   * form of: areaId, target, target parameters [, areaId, target, target parameters...].
@@ -707,6 +719,67 @@ function submitFormInBackground(form, areaId, submitUrl) {
     });
 }
 
+function containsErrorMessages(data) {
+    return data.ERROR_MESSAGE_LIST_ != undefined || data._ERROR_MESSAGE_ != undefined
+}
+function displayErrorMessages(data) {
+    if (!jQuery('#content-messages').length) {
+        //add this div just after app-navigation
+        if(jQuery('#content-main-section')){
+            jQuery('#content-main-section' ).before('<div id="content-messages" onclick="hideErrorContainer()"></div>');
+        }
+    }
+    jQuery('#content-messages').addClass('errorMessage');
+    if (data._ERROR_MESSAGE_LIST_ != undefined && data._ERROR_MESSAGE_ != undefined) {
+        jQuery('#content-messages' ).html(data._ERROR_MESSAGE_LIST_ + " " + data._ERROR_MESSAGE_);
+    } else if (data._ERROR_MESSAGE_LIST_ != undefined) {
+        jQuery('#content-messages' ).html(data._ERROR_MESSAGE_LIST_);
+    } else {
+        jQuery('#content-messages' ).html(data._ERROR_MESSAGE_);
+    }
+    showjGrowl();
+}
+
+function containsEventMessage(data) {
+    return data.EVENT_MESSAGE_LIST_ != undefined || data._EVENT_MESSAGE_ != undefined
+}
+function displayEventMessage(data) {
+    if (!jQuery('#content-messages').length) {
+        //add this div just after app-navigation
+        if(jQuery('#content-main-section')){
+            jQuery('#content-main-section' ).before('<div id="content-messages" onclick="hideErrorContainer()"></div>');
+        }
+    }
+    jQuery('#content-messages').addClass('eventMessage');
+    if (data._EVENT_MESSAGE_LIST_ != undefined && data._EVENT_MESSAGE_ != undefined) {
+        jQuery('#content-messages' ).html(data._EVENT_MESSAGE_LIST_ + " " + data._EVENT_MESSAGE_);
+    } else if (data._EVENT_MESSAGE_LIST_ != undefined) {
+        jQuery('#content-messages' ).html(data._EVENT_MESSAGE_LIST_);
+    } else {
+        jQuery('#content-messages' ).html(data._EVENT_MESSAGE_);
+    }
+    showjGrowl();
+}
+function clearErrorMessages() {
+    if (jQuery('#content-messages').length) {
+        jQuery('#content-messages').html('');
+        jQuery('#content-messages').removeClass('errorMessage').fadeIn('fast');
+    }
+    if (jQuery('#jGrowl').length) {
+        jQuery('[class$=errorMessageJGrowl]').hide();
+    }
+}
+
+function errorRetrievingResponseFromServer(xhr, status, exception) {
+    if(exception != 'abort') {
+        var errorMessage = '<p> No response from Apache OFBiz</p>';
+        if (status !== undefined) {
+            errorMessage += '<p> (state: ' + status + ')</p>';
+        }
+        displayErrorMessages({_ERROR_MESSAGE_: errorMessage});
+    }
+}
+
 /** Submit form, update multiple areas (HTML container elements).
  * @param formName The form name
  * @param areaCsvString The area CSV string. The CSV string is a flat array in the
@@ -714,43 +787,35 @@ function submitFormInBackground(form, areaId, submitUrl) {
 */
 function ajaxSubmitFormUpdateAreas(formName, areaCsvString) {
    waitSpinnerShow();
+
+   var $form = jQuery("form[name='" + formName + "']");
    hideErrorContainer = function() {
-       jQuery('#content-messages').html('');
-       jQuery('#content-messages').removeClass('errorMessage').fadeIn('fast');
+       clearErrorMessages()
    }
-   updateFunction = function(data) {
-       if (data._ERROR_MESSAGE_LIST_ != undefined || data._ERROR_MESSAGE_ != undefined) {
-           if (!jQuery('#content-messages').length) {
-              //add this div just after app-navigation
-              if(jQuery('#content-main-section')){
-                  jQuery('#content-main-section' ).before('<div id="content-messages" onclick="hideErrorContainer()"></div>');
-              }
+   updateFunction = function(data, status, response) {
+       if (response.getResponseHeader("content-type").indexOf("application/json") === -1) {
+           var areaId = areaCsvString.substring(0, areaCsvString.indexOf(','));
+           if (areaId === "") {
+               areaId = $form[0].target
            }
-           jQuery('#content-messages').addClass('errorMessage');
-          if (data._ERROR_MESSAGE_LIST_ != undefined && data._ERROR_MESSAGE_ != undefined) {
-              jQuery('#content-messages' ).html(data._ERROR_MESSAGE_LIST_ + " " + data._ERROR_MESSAGE_);
-          } else if (data._ERROR_MESSAGE_LIST_ != undefined) {
-              jQuery('#content-messages' ).html(data._ERROR_MESSAGE_LIST_);
-          } else {
-              jQuery('#content-messages' ).html(data._ERROR_MESSAGE_);
-          }
-          showjGrowl();
+           updateArea(areaId, data)
        } else {
-           if (jQuery('#content-messages').length) {
-               jQuery('#content-messages').html('');
-               jQuery('#content-messages').removeClass('errorMessage').fadeIn("fast");
+           if (containsErrorMessages(data)) {
+               displayErrorMessages(data)
+           } else {
+               clearErrorMessages()
+               if (containsEventMessage(data)) {
+                   displayEventMessage(data)
+               }
+               ajaxUpdateAreas(areaCsvString);
+               $form.trigger("closeCurrentModalAfterAjaxSubmitFormUpdateAreasInSuccess");
            }
-           ajaxUpdateAreas(areaCsvString);
        }
-       waitSpinnerHide();
    }
-
-   var $form = jQuery("form[name='" + formName + "']"),
-       data = null,
+   var data = null,
        processData = true,
        enctype = $form.attr("enctype"),
        contentType = "application/x-www-form-urlencoded; charset=UTF-8";
-
    if (enctype && enctype.indexOf("multipart") !== -1) {
        data = new FormData($form[0]);
        contentType = false;
@@ -765,8 +830,13 @@ function ajaxSubmitFormUpdateAreas(formName, areaCsvString) {
        url: $form.attr("action"),
        data: data,
        processData: processData,
-       success: function(data) {
-               updateFunction(data);
+       success: function(data, status, response) {
+           updateFunction(data, status, response);
+           waitSpinnerHide();
+       },
+       error: function(xhr, status, exception) {
+           errorRetrievingResponseFromServer(xhr, status, exception)
+           waitSpinnerHide();
        }
    });
 }
@@ -1240,8 +1310,21 @@ function setUserLayoutPreferences(userPrefGroupTypeId, userPrefTypeId, userPrefV
     });
 }
 
-function waitSpinnerShow() {
-    jSpinner = jQuery("#wait-spinner");
+/**
+ * if an id is present, return the area attendee the wait-spinner
+ * else use generic wait-spinner area
+ * @param id
+ * @returns {string}
+ */
+function resolveWaitSpinnerId(id) {
+    if (id === undefined) {
+        return 'wait-spinner';
+    }
+    return id + '-wait-spinner';
+}
+
+function waitSpinnerShow(id) {
+    jSpinner = jQuery("#" + resolveWaitSpinnerId(id));
     if (!jSpinner.length) return
 
     bdy = document.body;
@@ -1257,8 +1340,8 @@ function waitSpinnerShow() {
     jSpinner.show();
 }
 
-function waitSpinnerHide() {
-    jQuery("#wait-spinner").hide()
+function waitSpinnerHide(id) {
+    jQuery("#" + resolveWaitSpinnerId(id)).hide()
 }
 
 /**
@@ -1515,7 +1598,7 @@ var importLibrary = function() {
                 }
             })
         ).then(onSuccessFn).catch(onErrorFn || function (err) {
-            alert('Error:\n'+err+'\n\nFile(s): \n' + urls.join('\n'))
+            console.error('Error:\n'+err+'\n\nFile(s): \n' + urls.join('\n'))
         });
     }
 }();