From fd97ef3e7ef882aa85dfa17258995630f8f6c7d5 Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sun, 23 Apr 2023 18:37:20 +0200
Subject: [PATCH] #742 - avoid page reload inline save

---
 .../TransactionImportController.java          | 10 ++--
 .../resources/static/js/transactionImport.js  | 47 ++++++++++++++++++-
 .../transactions/transactionImportMacros.ftl  |  2 +-
 3 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionImportController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionImportController.java
index de5e23439..598e81653 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionImportController.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionImportController.java
@@ -285,14 +285,14 @@ public class TransactionImportController extends BaseController
 	}
 
 	@PostMapping("/{index}/newTransactionInPlace")
-	public String newTransactionInPlace(WebRequest request,
+	public String newTransactionInPlace(Model model, WebRequest request,
 										@PathVariable("index") Integer index,
 										@ModelAttribute("NewTransactionInPlace") CsvTransaction newCsvTransaction)
 	{
 		final Optional<CsvTransaction> transactionOptional = getTransactionByIndex(request, index);
 		if(transactionOptional.isEmpty())
 		{
-			return ReturnValues.REDIRECT_IMPORT;
+			throw new ResourceNotFoundException();
 		}
 
 		final CsvTransaction csvTransaction = transactionOptional.get();
@@ -303,7 +303,11 @@ public class TransactionImportController extends BaseController
 		final Transaction newTransaction = transactionImportService.createTransactionFromCsvTransaction(csvTransaction);
 		transactionService.getRepository().save(newTransaction);
 
-		return ReturnValues.REDIRECT_IMPORT;
+		model.addAttribute(ModelAttributes.CATEGORIES, categoryService.getAllEntitiesAsc());
+		model.addAttribute(TransactionModelAttributes.SUGGESTIONS_JSON, transactionService.getNameSuggestionsJson());
+		model.addAttribute(ModelAttributes.CSV_TRANSACTION, csvTransaction);
+		model.addAttribute(ModelAttributes.CSV_TRANSACTION_INDEX, index);
+		return ReturnValues.TRANSACTION_IMPORT_ROW;
 	}
 
 	private void removeAllAttributes(WebRequest request)
diff --git a/BudgetMasterServer/src/main/resources/static/js/transactionImport.js b/BudgetMasterServer/src/main/resources/static/js/transactionImport.js
index e4c29c1ec..f18ff6420 100644
--- a/BudgetMasterServer/src/main/resources/static/js/transactionImport.js
+++ b/BudgetMasterServer/src/main/resources/static/js/transactionImport.js
@@ -33,9 +33,52 @@ $(document).ready(function()
         }
     }
 
-    initCsvTransactionButtons();
+    initCsvTransactions();
 });
 
+function initCsvTransactions()
+{
+    initCsvTransactionForms();
+    initCsvTransactionButtons();
+}
+
+function initCsvTransactionForms()
+{
+    const forms = document.querySelectorAll('[name="NewTransactionInPlace"]');
+
+    for(let i = 0; i < forms.length; i++)
+    {
+        let form = forms[i];
+        $(form).submit(function(event)
+        {
+            const csvTransactionId = form.dataset.index;
+
+            $.ajax({
+                type: 'POST',
+                url: $(this).attr('action'),
+                data: new FormData(form),
+                processData: false,
+                contentType: false,
+                success: function(response)
+                {
+                    $('#transaction-import-row-' + csvTransactionId).replaceWith(response);
+                    initCsvTransactions();
+                },
+                error: function(response)
+                {
+                    M.toast({
+                        html: "Error saving transaction",
+                        classes: 'red'
+                    });
+                    console.error(response);
+                }
+            });
+
+            event.preventDefault();
+        });
+    }
+}
+
 function initCsvTransactionButtons()
 {
     const buttonsSkip = document.getElementsByClassName('button-request-transaction-import-skip');
@@ -71,7 +114,7 @@ function performCsvTransactionGetRequestWithoutReload(button, errorMessage)
         success: function(data)
         {
             $('#transaction-import-row-' + csvTransactionId).replaceWith(data);
-            initCsvTransactionButtons();
+            initCsvTransactions();
         },
         error: function(response)
         {
diff --git a/BudgetMasterServer/src/main/resources/templates/transactions/transactionImportMacros.ftl b/BudgetMasterServer/src/main/resources/templates/transactions/transactionImportMacros.ftl
index fae7d580a..cf03a21ed 100644
--- a/BudgetMasterServer/src/main/resources/templates/transactions/transactionImportMacros.ftl
+++ b/BudgetMasterServer/src/main/resources/templates/transactions/transactionImportMacros.ftl
@@ -189,7 +189,7 @@
 
 <#macro renderCsvTransaction csvTransaction index>
     <tr class="transaction-import-row <#if csvTransaction.getStatus().name() == 'SKIPPED'>transaction-import-row-skipped</#if>" id="transaction-import-row-${index}">
-        <form name="NewTransactionInPlace" method="POST" action="<@s.url '/transactionImport/' + index + '/newTransactionInPlace'/>">
+        <form name="NewTransactionInPlace" method="POST" action="<@s.url '/transactionImport/' + index + '/newTransactionInPlace'/>" data-index="${index}">
             <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
             <td data-order="${locale.getString(csvTransaction.getStatus().getLocalizationKey())}" data-search="${locale.getString(csvTransaction.getStatus().getLocalizationKey())}"><@statusBanner csvTransaction.getStatus()/></td>
             <td data-order="${csvTransaction.getDate()}" data-search="${csvTransaction.getDate()}">${csvTransaction.getDate()}</td>
-- 
GitLab