From eaae33fb7147accdcc126aa54a16f640f39cfcab Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sun, 14 Mar 2021 17:31:07 +0100
Subject: [PATCH] #419 - add upload inputs to modal + load available image
 async from server

---
 .../budgetmaster/images/MediaController.java  | 39 ++++++++++++++
 .../budgetmaster/utils/Mappings.java          |  2 +-
 .../resources/languages/base_de.properties    |  2 +
 .../resources/languages/base_en.properties    |  2 +
 src/main/resources/static/css/accounts.css    |  4 ++
 src/main/resources/static/js/accounts.js      | 53 ++++++++++++++-----
 .../templates/accounts/accountFunctions.ftl   | 46 +++++++++++-----
 .../templates/accounts/availableImages.ftl    | 11 ++++
 .../templates/accounts/newAccount.ftl         |  2 +-
 9 files changed, 134 insertions(+), 27 deletions(-)
 create mode 100644 src/main/java/de/deadlocker8/budgetmaster/images/MediaController.java
 create mode 100644 src/main/resources/templates/accounts/availableImages.ftl

diff --git a/src/main/java/de/deadlocker8/budgetmaster/images/MediaController.java b/src/main/java/de/deadlocker8/budgetmaster/images/MediaController.java
new file mode 100644
index 000000000..f3db7d452
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/images/MediaController.java
@@ -0,0 +1,39 @@
+package de.deadlocker8.budgetmaster.images;
+
+import de.deadlocker8.budgetmaster.controller.BaseController;
+import de.deadlocker8.budgetmaster.utils.Mappings;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.multipart.MultipartFile;
+
+@Controller
+@RequestMapping(Mappings.MEDIA)
+public class MediaController extends BaseController
+{
+	private final ImageService imageService;
+
+	@Autowired
+	public MediaController(ImageService imageService)
+	{
+		this.imageService = imageService;
+	}
+
+	@GetMapping("/getAvailableImages")
+	public String getAvailableImages(Model model)
+	{
+		model.addAttribute("availableImages", imageService.getRepository().findAll());
+		return "accounts/availableImages";
+	}
+
+	@PostMapping("uploadImage")
+	public String handleImagePost(@RequestParam("file") MultipartFile file)
+	{
+		imageService.saveImageFile(file);
+		return "redirect:/accounts";
+	}
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/utils/Mappings.java b/src/main/java/de/deadlocker8/budgetmaster/utils/Mappings.java
index d1b66d84b..10985ab6a 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/utils/Mappings.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/utils/Mappings.java
@@ -14,7 +14,7 @@ public final class Mappings
 	public static final String ERROR = "/error";
 	public static final String FILTER = "/filter";
 	public static final String HOTKEYS = "/hotkeys";
-	public static final String IMAGES = "/media";
+	public static final String MEDIA = "/media";
 	public static final String LOGIN = "/login";
 	public static final String REPORTS = "/reports";
 	public static final String SEARCH = "/search";
diff --git a/src/main/resources/languages/base_de.properties b/src/main/resources/languages/base_de.properties
index 140ca2593..374f16f31 100644
--- a/src/main/resources/languages/base_de.properties
+++ b/src/main/resources/languages/base_de.properties
@@ -262,6 +262,8 @@ settings.search.itemsPerPage=Anzahl der Suchergebnisse pro Seite
 account.new.label.name=Name
 account.new.label.icon=Icon
 account.new.icon.placeholder=Hier klicken um ein Icon auszuwählen
+account.new.icon.upload.choose.file=Datei auswählen
+account.new.icon.upload=Hochladen
 account.default.name=Standardkonto
 account.all=Alle Konten
 account.budget.asof=Stand
diff --git a/src/main/resources/languages/base_en.properties b/src/main/resources/languages/base_en.properties
index 9bff555fd..48e7dfb92 100644
--- a/src/main/resources/languages/base_en.properties
+++ b/src/main/resources/languages/base_en.properties
@@ -262,6 +262,8 @@ settings.search.itemsPerPage=Number of search results per page
 account.new.label.name=Name
 account.new.label.icon=Icon
 account.new.icon.placeholder=Click here to select an icon
+account.new.icon.upload.choose.file=Choose file
+account.new.icon.upload=Upload
 account.default.name=Default Account
 account.all=All Accounts
 account.budget.asof=as of
diff --git a/src/main/resources/static/css/accounts.css b/src/main/resources/static/css/accounts.css
index 6d352cc32..c4ca1f394 100644
--- a/src/main/resources/static/css/accounts.css
+++ b/src/main/resources/static/css/accounts.css
@@ -39,3 +39,7 @@
 .account-icon-preview-icon {
     height: 5rem;
 }
+
+#account-icon-preview:hover {
+    cursor: pointer;
+}
diff --git a/src/main/resources/static/js/accounts.js b/src/main/resources/static/js/accounts.js
index 966e6ba28..150f2e8a7 100644
--- a/src/main/resources/static/js/accounts.js
+++ b/src/main/resources/static/js/accounts.js
@@ -41,18 +41,6 @@ $(document).ready(function()
         document.getElementById("hidden-input-account-icon").value = iconId;
     });
 
-    // select an icon option
-    $('.account-icon-option').click(function()
-    {
-        let allIconOptions = document.querySelectorAll('.account-icon-option');
-        for(let i = 0; i < allIconOptions.length; i++)
-        {
-            allIconOptions[i].classList.remove('selected');
-        }
-
-        this.classList.add('selected');
-    });
-
     if($('#modalAccountIconSelect').length)
     {
         let modalAccountIconSelect = document.getElementById('modalAccountIconSelect');
@@ -63,4 +51,43 @@ $(document).ready(function()
             }
         });
     }
-});
\ No newline at end of file
+
+    $('#account-icon-preview').click(function()
+    {
+        openSelectAccountIconModal(this);
+    });
+});
+
+function openSelectAccountIconModal(item)
+{
+    $.ajax({
+        type: 'GET',
+        url: $(item).attr('data-url'),
+        data: {},
+        success: function(data)
+        {
+            let modalID = '#modalAccountIconSelect';
+            $('#available-images').html(data);
+
+            // select an icon option
+            $('.account-icon-option').click(function()
+            {
+               selectIcon(this);
+            });
+
+            $(modalID).modal();
+            $(modalID).modal('open');
+        }
+    });
+}
+
+function selectIcon(item)
+{
+    let allIconOptions = document.querySelectorAll('.account-icon-option');
+    for(let i = 0; i < allIconOptions.length; i++)
+    {
+        allIconOptions[i].classList.remove('selected');
+    }
+
+    item.classList.add('selected');
+}
\ No newline at end of file
diff --git a/src/main/resources/templates/accounts/accountFunctions.ftl b/src/main/resources/templates/accounts/accountFunctions.ftl
index a3ff14c09..ba1afc9e1 100644
--- a/src/main/resources/templates/accounts/accountFunctions.ftl
+++ b/src/main/resources/templates/accounts/accountFunctions.ftl
@@ -2,11 +2,41 @@
 
 <#macro modalAccountIconSelect>
     <div id="modalAccountIconSelect" class="modal modal-fixed-footer background-color">
-        <div class="modal-content">
+        <div class="modal-content center-align">
             <div class="row">
-                <#list availableImages as image>
-                    <@accountIconOption image/>
-                </#list>
+                <div class="col s12">
+                    <div class="headline">Upload image</div>
+                </div>
+            </div>
+
+            <div class="row">
+                <form id="form-upload-account-image" method="POST" action="/media" enctype="multipart/form-data">
+                    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
+                    <div class="file-field input-field col s12">
+                        <div class="container">
+                            <div class="btn background-blue">
+                                <i class="material-icons left">folder</i>
+                                ${locale.getString("account.new.icon.upload.choose.file")}
+                                <input id="inputUploadFile" type="file" accept="image/png, image/jpeg" name="file">
+                            </div>
+                            <div class="file-path-wrapper" id="abc">
+                                <input id="def" class="file-path validate" type="text">
+                            </div>
+                            <@header.buttonSubmit name='action' icon='upload' localizationKey='account.new.icon.upload' classes="right"/>
+                        </div>
+                    </div>
+                </form>
+            </div>
+
+            <hr>
+
+            <div class="row">
+                <div class="col s12">
+                     <div class="headline">Available images</div>
+                </div>
+            </div>
+
+            <div class="row" id="available-images">
             </div>
         </div>
         <div class="modal-footer background-color">
@@ -15,11 +45,3 @@
         </div>
     </div>
 </#macro>
-
-<#macro accountIconOption image>
-    <div class="col s4 m2 l2 account-icon-option-column">
-        <div class="account-icon-option">
-            <img src="${image.getBase64EncodedImage()}" class="account-icon-preview" data-image-id="${image.getID()}"/>
-        </div>
-    </div>
-</#macro>
\ No newline at end of file
diff --git a/src/main/resources/templates/accounts/availableImages.ftl b/src/main/resources/templates/accounts/availableImages.ftl
new file mode 100644
index 000000000..504abbfbc
--- /dev/null
+++ b/src/main/resources/templates/accounts/availableImages.ftl
@@ -0,0 +1,11 @@
+<#list availableImages as image>
+    <@imageOption image 'account-icon'/>
+</#list>
+
+<#macro imageOption image classPrefix>
+    <div class="col s4 m2 l2 ${classPrefix}-option-column">
+        <div class="${classPrefix}-option">
+            <img src="${image.getBase64EncodedImage()}" class="${classPrefix}-preview" data-image-id="${image.getID()}"/>
+        </div>
+    </div>
+</#macro>
\ No newline at end of file
diff --git a/src/main/resources/templates/accounts/newAccount.ftl b/src/main/resources/templates/accounts/newAccount.ftl
index 674668fd6..fd38c5124 100644
--- a/src/main/resources/templates/accounts/newAccount.ftl
+++ b/src/main/resources/templates/accounts/newAccount.ftl
@@ -53,7 +53,7 @@
                                 <label class="input-label" for="account-icon">${locale.getString("account.new.label.icon")}</label>
 
                                 <div id="account-icon" class="valign-wrapper">
-                                    <a href="#modalAccountIconSelect" id="account-icon-preview" class="modal-trigger">
+                                    <a id="account-icon-preview" data-url="<@s.url '/media/getAvailableImages'/>">
                                         <img id="account-icon-preview-icon" src="<#if account.getIcon()??>${account.getIcon().getBase64EncodedImage()}</#if>" class="account-icon-preview <#if account.getIcon()?? == false>hidden</#if>"/>
                                         <div id="account-icon-placeholder" class="<#if account.getIcon()??>hidden</#if>">${locale.getString("account.new.icon.placeholder")}</div>
                                     </a>
-- 
GitLab