From f5056af3291f634cecdc276d67e9840a5411181f Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sun, 15 Jan 2023 11:14:29 +0100
Subject: [PATCH] #724 - make table sortable and searchable

---
 BudgetMasterServer/pom.xml                    |  6 +++
 .../csvimport/CsvTransactionStatus.java       | 18 +++++--
 .../resources/languages/base_de.properties    |  2 +-
 .../resources/languages/base_en.properties    |  2 +-
 .../static/css/transactionImport.css          | 46 ++++++++++++++++-
 .../resources/static/js/transactionImport.js  | 14 ++++++
 .../transactions/transactionImport.ftl        | 50 +++++++++++--------
 7 files changed, 111 insertions(+), 27 deletions(-)

diff --git a/BudgetMasterServer/pom.xml b/BudgetMasterServer/pom.xml
index 43ab50c8f..3235b531d 100644
--- a/BudgetMasterServer/pom.xml
+++ b/BudgetMasterServer/pom.xml
@@ -41,6 +41,7 @@
         <vanilla-picker.version>2.12.1</vanilla-picker.version>
         <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version>
         <opencsv.version>5.3</opencsv.version>
+        <datatables.version>1.13.1</datatables.version>
 
         <project.outputDirectory>${project.build.directory}/../build/${project.version}</project.outputDirectory>
         <project.artifactName>${project.artifactId}-v${project.version}</project.artifactName>
@@ -185,6 +186,11 @@
             <artifactId>vanilla-picker</artifactId>
             <version>${vanilla-picker.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.webjars</groupId>
+            <artifactId>datatables</artifactId>
+            <version>${datatables.version}</version>
+        </dependency>
 
 
         <!-- selenium -->
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/CsvTransactionStatus.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/CsvTransactionStatus.java
index 0b1ff42b6..5ea895158 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/CsvTransactionStatus.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/CsvTransactionStatus.java
@@ -2,7 +2,19 @@ package de.deadlocker8.budgetmaster.transactions.csvimport;
 
 public enum CsvTransactionStatus
 {
-	PENDING,
-	IMPORTED,
-	SKIPPED;
+	PENDING("transactions.import.status.pending"),
+	IMPORTED("transactions.import.status.imported"),
+	SKIPPED("transactions.import.status.skipped");
+
+	private final String localizationKey;
+
+	CsvTransactionStatus(String localizationKey)
+	{
+		this.localizationKey = localizationKey;
+	}
+
+	public String getLocalizationKey()
+	{
+		return localizationKey;
+	}
 }
diff --git a/BudgetMasterServer/src/main/resources/languages/base_de.properties b/BudgetMasterServer/src/main/resources/languages/base_de.properties
index 64d996e67..42a0e893e 100644
--- a/BudgetMasterServer/src/main/resources/languages/base_de.properties
+++ b/BudgetMasterServer/src/main/resources/languages/base_de.properties
@@ -1,7 +1,7 @@
 locale=de
 
 # DEFAULT
-credits=Verwendete Schriftarten: Roboto<br>Verwendete Bibliotheken:<br>spring-boot-starter-parent 2.7.0<br>spring-boot-devtools 2.7.0<br>spring-boot-starter-web 2.7.0<br>spring-boot-starter-test 2.7.0<br>spring-boot-starter-security 2.7.0<br>spring-boot-starter-tomcat 2.7.0<br>spring-boot-starter-freemarker 2.7.0<br>spring-boot-starter-validation 2.7.0<br>h2 1.4.199<br>maven-surefire-plugin 2.22.2<br>launch4j-maven-plugin 1.7.25<br>jquery 3.6.1<br>materialize 1.0.0<br>fontawesome 6.2.0<br>Google Material Icons<br>Vanilla-picker 2.12.1<br>SortableJS 1.15.0<br>jlibs 3.2.0<br>itextpdf 5.5.13.3<br>mousetrap 1.6.5<br>plotly 2.16.1<br>momentjs 2.29.4<br>codemirror 5.62.2<br>webjars-locator 0.46<br>libUtils 3.2.7<br>libStorage 3.2.3<br>natorder 1.1.3<br>jgit 6.4.0.202211300538-r<br>opencsv 5.3<br>
+credits=Verwendete Schriftarten: Roboto<br>Verwendete Bibliotheken:<br>spring-boot-starter-parent 2.7.0<br>spring-boot-devtools 2.7.0<br>spring-boot-starter-web 2.7.0<br>spring-boot-starter-test 2.7.0<br>spring-boot-starter-security 2.7.0<br>spring-boot-starter-tomcat 2.7.0<br>spring-boot-starter-freemarker 2.7.0<br>spring-boot-starter-validation 2.7.0<br>h2 1.4.199<br>maven-surefire-plugin 2.22.2<br>launch4j-maven-plugin 1.7.25<br>jquery 3.6.1<br>materialize 1.0.0<br>fontawesome 6.2.0<br>Google Material Icons<br>Vanilla-picker 2.12.1<br>SortableJS 1.15.0<br>jlibs 3.2.0<br>itextpdf 5.5.13.3<br>mousetrap 1.6.5<br>plotly 2.16.1<br>momentjs 2.29.4<br>codemirror 5.62.2<br>webjars-locator 0.46<br>libUtils 3.2.7<br>libStorage 3.2.3<br>natorder 1.1.3<br>jgit 6.4.0.202211300538-r<br>opencsv 5.3<br>datatables 1.13.1<br>
 folder=Deadlocker/BudgetMaster
 roadmap.url=https://roadmaps.thecodelabs.de/roadmap/1
 github.url=https://github.com/deadlocker8/BudgetMaster
diff --git a/BudgetMasterServer/src/main/resources/languages/base_en.properties b/BudgetMasterServer/src/main/resources/languages/base_en.properties
index 56d9d38d2..ad79259a6 100644
--- a/BudgetMasterServer/src/main/resources/languages/base_en.properties
+++ b/BudgetMasterServer/src/main/resources/languages/base_en.properties
@@ -1,7 +1,7 @@
 locale=en
 
 # DEFAULT
-credits=Fonts used: Roboto<br>Libraries used:<br>spring-boot-starter-parent 2.7.0<br>spring-boot-devtools 2.7.0<br>spring-boot-starter-web 2.7.0<br>spring-boot-starter-test 2.7.0<br>spring-boot-starter-security 2.7.0<br>spring-boot-starter-tomcat 2.7.0<br>spring-boot-starter-freemarker 2.7.0<br>spring-boot-starter-validation 2.7.0<br>h2 1.4.199<br>maven-surefire-plugin 2.22.2<br>launch4j-maven-plugin 1.7.25<br>jquery 3.6.1<br>materialize 1.0.0<br>fontawesome 6.2.0<br>Google Material Icons<br>Vanilla-picker 2.12.1<br>SortableJS 1.15.0<br>jlibs 3.2.0<br>itextpdf 5.5.13.3<br>mousetrap 1.6.5<br>plotly 2.16.1<br>momentjs 2.29.4<br>codemirror 5.62.2<br>webjars-locator 0.46<br>libUtils 3.2.7<br>libStorage 3.2.3<br>natorder 1.1.3<br>jgit 6.4.0.202211300538-r<br>opencsv 5.3<br>
+credits=Fonts used: Roboto<br>Libraries used:<br>spring-boot-starter-parent 2.7.0<br>spring-boot-devtools 2.7.0<br>spring-boot-starter-web 2.7.0<br>spring-boot-starter-test 2.7.0<br>spring-boot-starter-security 2.7.0<br>spring-boot-starter-tomcat 2.7.0<br>spring-boot-starter-freemarker 2.7.0<br>spring-boot-starter-validation 2.7.0<br>h2 1.4.199<br>maven-surefire-plugin 2.22.2<br>launch4j-maven-plugin 1.7.25<br>jquery 3.6.1<br>materialize 1.0.0<br>fontawesome 6.2.0<br>Google Material Icons<br>Vanilla-picker 2.12.1<br>SortableJS 1.15.0<br>jlibs 3.2.0<br>itextpdf 5.5.13.3<br>mousetrap 1.6.5<br>plotly 2.16.1<br>momentjs 2.29.4<br>codemirror 5.62.2<br>webjars-locator 0.46<br>libUtils 3.2.7<br>libStorage 3.2.3<br>natorder 1.1.3<br>jgit 6.4.0.202211300538-r<br>opencsv 5.3<br>datatables 1.13.1<br>
 folder=Deadlocker/BudgetMaster
 roadmap.url=https://roadmaps.thecodelabs.de/roadmap/2
 github.url=https://github.com/deadlocker8/BudgetMaster
diff --git a/BudgetMasterServer/src/main/resources/static/css/transactionImport.css b/BudgetMasterServer/src/main/resources/static/css/transactionImport.css
index 276482543..586c72d4c 100644
--- a/BudgetMasterServer/src/main/resources/static/css/transactionImport.css
+++ b/BudgetMasterServer/src/main/resources/static/css/transactionImport.css
@@ -10,7 +10,7 @@
 }
 
 .transaction-import-text-with-icon i {
-   margin-right: 1.3rem;
+    margin-right: 1.3rem;
 }
 
 .transaction-import-row-skipped {
@@ -19,4 +19,48 @@
 
 #table-transaction-rows td {
     padding: 5px;
+}
+
+#transaction-import-list {
+    width: 90%;
+    margin: auto;
+}
+
+#table-transaction-rows {
+    width: 100%;
+}
+
+#table-transaction-rows_filter input {
+    height: 1rem;
+    border: none;
+    border-radius: 0;
+}
+
+/* label focus color */
+#table-transaction-rows_filter input[type=search]:focus + label {
+    color: var(--color-blue) !important;
+}
+
+/* label underline focus color */
+#table-transaction-rows_filter input[type=search]:focus:not(.invalid) {
+    border-bottom: 1px solid var(--color-blue) !important;
+    box-shadow: 0 1px 0 0 #CCCCCC !important;
+}
+
+/* input text color */
+[data-theme="dark"] #table-transaction-rows_filter input[type=search] {
+    color: var(--color-white);
+    border-bottom: 1px solid var(--color-white);
+    box-shadow: 0 1px 0 0 #CCCCCC;
+}
+
+/* input label color */
+[data-theme="dark"] #table-transaction-rows_filter input[type=search] + label {
+    color: var(--color-white) !important;
+}
+
+/* label underline focus color */
+[data-theme="dark"] #table-transaction-rows_filter input[type=search]:focus:not(.invalid) {
+    border-bottom: 1px solid var(--color-blue) !important;
+    box-shadow: 0 1px 0 0 var(--color-blue) !important;
 }
\ No newline at end of file
diff --git a/BudgetMasterServer/src/main/resources/static/js/transactionImport.js b/BudgetMasterServer/src/main/resources/static/js/transactionImport.js
index 095abfcaa..99038e7b6 100644
--- a/BudgetMasterServer/src/main/resources/static/js/transactionImport.js
+++ b/BudgetMasterServer/src/main/resources/static/js/transactionImport.js
@@ -6,4 +6,18 @@ $(document).ready(function()
     });
 
     $('.collapsible').collapsible();
+
+    $('#table-transaction-rows').DataTable({
+        paging: false,
+        order: [[1, 'desc']],
+        info: false,
+        scrollX: true,
+        scrollY: true,
+        columnDefs: [
+            { width: '30%', targets: 2},
+            { width: '30%', targets: 3},
+            { orderable: false, targets:  5}
+        ],
+        language: { search: '' , searchPlaceholder: localizedSearch},
+    });
 });
\ No newline at end of file
diff --git a/BudgetMasterServer/src/main/resources/templates/transactions/transactionImport.ftl b/BudgetMasterServer/src/main/resources/templates/transactions/transactionImport.ftl
index e848d9ef3..29505a74c 100644
--- a/BudgetMasterServer/src/main/resources/templates/transactions/transactionImport.ftl
+++ b/BudgetMasterServer/src/main/resources/templates/transactions/transactionImport.ftl
@@ -8,6 +8,7 @@
         <@header.style "transactionImport"/>
         <@header.style "collapsible"/>
         <#import "/spring.ftl" as s>
+        <link rel="stylesheet" href="<@s.url '/webjars/datatables/1.13.1/css/jquery.dataTables.min.css'/>"/>
     </head>
     <@header.body>
         <#import "../helpers/navbar.ftl" as navbar>
@@ -58,9 +59,14 @@
             </div>
         </main>
 
+        <script>
+            localizedSearch = '${locale.getString("search")}';
+        </script>
+
         <!--  Scripts-->
         <#import "../helpers/scripts.ftl" as scripts>
         <@scripts.scripts/>
+        <script src="<@s.url '/webjars/datatables/1.13.1/js/jquery.dataTables.min.js'/>"></script>
         <script src="<@s.url '/js/transactionImport.js'/>"></script>
     </@header.body>
 </html>
@@ -211,20 +217,24 @@
 </#macro>
 
 <#macro renderCsvTransactions>
-    <div class="container" id="transaction-import-list">
+    <div id="transaction-import-list">
         <table class="bordered centered" id="table-transaction-rows">
-            <tr>
-                <td class="bold">${locale.getString("transactions.import.status")}</td>
-                <td class="bold">${locale.getString("transaction.new.label.date")}</td>
-                <td class="bold">${locale.getString("transaction.new.label.name")}</td>
-                <td class="bold">${locale.getString("transaction.new.label.description")}</td>
-                <td class="bold">${locale.getString("transaction.new.label.amount")}</td>
-                <td class="bold">${locale.getString("transactions.import.actions")}</td>
-            </tr>
+            <thead>
+                <tr>
+                    <td class="bold">${locale.getString("transactions.import.status")}</td>
+                    <td class="bold">${locale.getString("transaction.new.label.date")}</td>
+                    <td class="bold">${locale.getString("transaction.new.label.name")}</td>
+                    <td class="bold">${locale.getString("transaction.new.label.description")}</td>
+                    <td class="bold">${locale.getString("transaction.new.label.amount")}</td>
+                    <td class="bold">${locale.getString("transactions.import.actions")}</td>
+                </tr>
+            </thead>
 
-            <#list csvTransactions as csvTransaction>
-                <@renderCsvRow csvTransaction csvTransaction?index/>
-            </#list>
+            <tbody>
+                <#list csvTransactions as csvTransaction>
+                    <@renderCsvRow csvTransaction csvTransaction?index/>
+                </#list>
+            </tbody>
         </table>
     </div>
 </#macro>
@@ -233,24 +243,24 @@
     <tr class="<#if csvTransaction.getStatus().name() == 'SKIPPED'>transaction-import-row-skipped</#if>">
         <form name="NewTransactionInPlace" method="POST" action="<@s.url '/transactionImport/' + index + '/newTransactionInPlace'/>">
             <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
-            <td><@statusBanner csvTransaction.getStatus()/></td>
-            <td>${csvTransaction.getDate()}</td>
-            <td>
+            <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>
+            <td data-order="${csvTransaction.getName()}" data-search="${csvTransaction.getName()}">
                 <div class="input-field no-margin-top no-margin-bottom">
                     <input class="no-margin-bottom" type="text" name="name" required value="${csvTransaction.getName()}">
                 </div>
             </td>
-            <td>
+            <td data-order="${csvTransaction.getDescription()}" data-search="${csvTransaction.getDescription()}">
                 <div class="input-field no-margin-top no-margin-bottom">
                     <input class="no-margin-bottom" type="text" name="description" value="${csvTransaction.getDescription()}">
                 </div>
             </td>
-            <td>${currencyService.getCurrencyString(csvTransaction.getAmount())}</td>
+            <td data-order="${currencyService.getCurrencyString(csvTransaction.getAmount())}" data-search="${currencyService.getCurrencyString(csvTransaction.getAmount())}">${currencyService.getCurrencyString(csvTransaction.getAmount())}</td>
             <td>
                 <@header.buttonSubmit name='action' icon='save' localizationKey='' classes='text-white'/>&nbsp;
                 <div class="fixed-action-btn edit-transaction-button">
                     <a class="btn-floating btn-flat waves-effect waves-light no-padding text-default edit-transaction-button-link">
-                        <i class="material-icons">edit</i>
+                        <i class="material-icons text-default">edit</i>
                     </a>
                     <ul class="new-transaction-button-list">
                         <li>
@@ -303,19 +313,17 @@
 </#macro>
 
 <#macro statusBanner status>
+    <#assign bannerText=locale.getString(status.getLocalizationKey())>
     <#if status.name() == "PENDING">
         <#assign bannerClasses="background-blue text-white">
-        <#assign bannerText=locale.getString("transactions.import.status.pending")>
     <#elseif status.name() == "IMPORTED">
         <#assign bannerClasses="background-green text-white">
-        <#assign bannerText=locale.getString("transactions.import.status.imported")>
     <#elseif status.name() == "SKIPPED">
         <#if settings.isUseDarkTheme()>
             <#assign bannerClasses="background-grey text-black">
         <#else>
             <#assign bannerClasses="background-grey text-white">
         </#if>
-        <#assign bannerText=locale.getString("transactions.import.status.skipped")>
     </#if>
 
     <div class="banner ${bannerClasses}">${bannerText}</div>
-- 
GitLab