diff --git a/src/main/java/de/deadlocker8/budgetmaster/controller/PaymentController.java b/src/main/java/de/deadlocker8/budgetmaster/controller/PaymentController.java index 9dbf918ab3bc2b882453f24f7c2452f1ba8e6767..eba53a48715adbd601194467db5463eb045bc086 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/controller/PaymentController.java +++ b/src/main/java/de/deadlocker8/budgetmaster/controller/PaymentController.java @@ -18,6 +18,7 @@ import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; import java.util.List; @@ -97,7 +98,7 @@ public class PaymentController extends BaseController { PaymentValidator userValidator = new PaymentValidator(); userValidator.validate(payment, bindingResult); - + if(bindingResult.hasErrors()) { model.addAttribute("error", bindingResult); @@ -115,6 +116,17 @@ public class PaymentController extends BaseController { payment.setAmount(Math.abs(payment.getAmount())); } + + List<Tag> tags = payment.getTags(); + if(tags != null) + { + payment.setTags(new ArrayList<>()); + for(Tag currentTag : tags) + { + addTagToPayment(currentTag.getName(), payment); + } + } + paymentRepository.save(payment); } diff --git a/src/main/java/de/deadlocker8/budgetmaster/repositories/TagRepository.java b/src/main/java/de/deadlocker8/budgetmaster/repositories/TagRepository.java index 4b766a433a25f49d850848f79d9deba087b2bf68..21fb2b22ff5e6fa73eb9718ac4654c3de3adce27 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/repositories/TagRepository.java +++ b/src/main/java/de/deadlocker8/budgetmaster/repositories/TagRepository.java @@ -3,8 +3,12 @@ package de.deadlocker8.budgetmaster.repositories; import de.deadlocker8.budgetmaster.entities.Tag; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface TagRepository extends JpaRepository<Tag, Integer> { Tag findByName(String name); + + List<Tag> findAllByOrderByNameAsc(); } \ No newline at end of file diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/HelpersService.java b/src/main/java/de/deadlocker8/budgetmaster/services/HelpersService.java index 50dc36ec74e7c3930dfa7f47d94724165a7b7381..e6ff8bde1d3b50c5b0e2846949ad286534f0d878 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/services/HelpersService.java +++ b/src/main/java/de/deadlocker8/budgetmaster/services/HelpersService.java @@ -1,6 +1,8 @@ package de.deadlocker8.budgetmaster.services; +import de.deadlocker8.budgetmaster.entities.Tag; import de.deadlocker8.budgetmaster.repositories.SettingsRepository; +import de.deadlocker8.budgetmaster.repositories.TagRepository; import de.deadlocker8.budgetmaster.utils.Colors; import de.deadlocker8.budgetmaster.utils.Strings; import org.joda.time.DateTime; @@ -14,6 +16,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.text.DecimalFormat; import java.util.ArrayList; +import java.util.List; @Service public class HelpersService @@ -21,6 +24,9 @@ public class HelpersService private final DecimalFormat NUMBER_FORMAT = new DecimalFormat("0.00"); @Autowired private SettingsRepository settingsRepository; + + @Autowired + private TagRepository tagRepository; public String getCurrencyString(int amount) { @@ -135,4 +141,9 @@ public class HelpersService return categoryColors; } + + public List<Tag> getAllTags() + { + return tagRepository.findAllByOrderByNameAsc(); + } } \ No newline at end of file diff --git a/src/main/resources/languages/_de.properties b/src/main/resources/languages/_de.properties index e1b6c25b143a54dcfaae129e1f912ef59214368f..89eed348f199725774057fece31e261834590944 100644 --- a/src/main/resources/languages/_de.properties +++ b/src/main/resources/languages/_de.properties @@ -78,7 +78,7 @@ url.placeholder=z.B. https://yourdomain.de currency.placeholder=z.B. \u20AC, CHF, $ trusted.hosts.placeholder=z.B. localhost undefined=unbekannt -tagfield.placeholder=Neuen Tag hier eingeben +tagfield.placeholder=Tag hier eingeben shortcut.dev.console=F12 local.server.status.ok=Server ist gestartet. local.server.status.not.started=Server konnte nicht gestartet werden. diff --git a/src/main/resources/languages/_en.properties b/src/main/resources/languages/_en.properties index 6ee66d074aa603f3652b83d71b0d5d4b8bf32262..a5afdd46aa9afca1a0d3b646e706818554b9cae1 100644 --- a/src/main/resources/languages/_en.properties +++ b/src/main/resources/languages/_en.properties @@ -78,7 +78,7 @@ url.placeholder=e.g. https://yourdomain.de currency.placeholder=e.g. \u20AC, CHF, $ trusted.hosts.placeholder=e.g. localhost undefined=undefined -tagfield.placeholder=Enter new Tag here +tagfield.placeholder=Enter Tag here shortcut.dev.console=F12 local.server.status.ok=Server is running. local.server.status.not.started=Server couldn't be started. diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index f2dffbc9ba827f4cee95edb51fc3d062d953edd0..d17367662c3bbde3f5db5fb6de7f3b0f50981e86 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -111,6 +111,28 @@ main { box-shadow: 0 1px 0 0 #CCCCCC !important; } +/* label focus color */ +.input-field textarea:focus + label { + color: #2E79B9 !important; +} + +/* label underline focus color */ +.input-field textarea:focus { + border-bottom: 1px solid #2E79B9 !important; + box-shadow: 0 1px 0 0 #CCCCCC !important; +} + +/* label focus color */ +.chips-label.active { + color: #2E79B9 !important; +} + +/* label chips underline focus color */ +.chips.focus { + border-bottom: 1px solid #2E79B9 !important; + box-shadow: 0 1px 0 0 #CCCCCC !important; +} + .main-card { margin: auto; width: 95%; @@ -266,6 +288,15 @@ ul.dropdown-content.select-dropdown li.selected { background-color: #2E79B9; } +/* chips autocomplete dropdown */ +.autocomplete-content li span{ + color: #2E79B9; +} + +.autocomplete-content li:hover { + background-color: rgba(0, 0, 0, 0.15); +} + .hidden { display: none; } diff --git a/src/main/resources/static/js/payments.js b/src/main/resources/static/js/payments.js index 335b7c3e10c09fad2ada29bc35c2939af8c677e5..7f3ac386437009a695b511950c00bcb9c85797ed 100644 --- a/src/main/resources/static/js/payments.js +++ b/src/main/resources/static/js/payments.js @@ -40,6 +40,27 @@ $( document ).ready(function() { validateAmount($(this).val()); }); } + + if($(".chips-autocomplete").length) + { + $('.chips-autocomplete').material_chip({ + autocompleteOptions: { + data: tagAutoComplete, + limit: Infinity, + minLength: 1 + }, + placeholder: tagsPlaceholder + }); + } + + // prevent form submit on enter (otherwise tag functionality will be hard to use) + $(document).on("keypress", 'form', function (e) { + var code = e.keyCode || e.which; + if (code === 13) { + e.preventDefault(); + return false; + } + }); }); AMOUNT_REGEX = new RegExp("^-?\\d+(,\\d+)?(\\.\\d+)?$"); @@ -71,4 +92,20 @@ function validateAmount(text) var amount = parseInt(parseFloat(text.replace(",", ".")) * 100); document.getElementById("hidden-payment-amount").value = amount; } +} + +function validateForm() +{ + var tags = $('.chips-autocomplete').material_chip('data'); + var parent = document.getElementById("hidden-payment-tags"); + for(var i = 0; i < tags.length; i++) + { + var input = document.createElement("input"); + input.setAttribute("type", "hidden"); + input.setAttribute("name", "tags[" + i + "].name"); + input.setAttribute("value", tags[i].tag); + parent.appendChild(input); + } + + return true; } \ No newline at end of file diff --git a/src/main/resources/templates/payments/newPayment.ftl b/src/main/resources/templates/payments/newPayment.ftl index 15822adced331622c2283c8b9e4df55ca95ecbec..ca2e3df763bca3ae4881b3fa0b78b4cb7cdc64d7 100644 --- a/src/main/resources/templates/payments/newPayment.ftl +++ b/src/main/resources/templates/payments/newPayment.ftl @@ -17,7 +17,7 @@ </div> <div class="container"> <#import "../validation.ftl" as validation> - <form name="NewPayment" action="/payments/newPayment" method="post"> + <form name="NewPayment" action="/payments/newPayment" method="post" onsubmit="return validateForm()"> <input type="hidden" name="ID" value="<#if payment.getID()??>${payment.getID()}</#if>"> <div class="row"> <div class="s12 m12 l8 offset-l2 center-align"> @@ -61,6 +61,7 @@ </select> <label for="payment-category">${locale.getString("payment.new.label.category")}</label> </div> + <div id="hidden-payment-tags"></div> </div> <div class="row"> <div class="input-field col s12 m12 l8 offset-l2"> @@ -74,6 +75,21 @@ <label for="payment-description">${locale.getString("payment.new.label.description")}</label> </div> </div> + <div class="row"> + <div class="col s12 m12 l8 offset-l2"> + <label class="chips-label" for="payment-chips">${locale.getString("payment.new.label.tags")}</label> + <div id="payment-chips" class="chips chips-placeholder chips-autocomplete"> + <#if payment.getTags()??> + <#list payment.getTags() as tag> + <div class="chip"> + ${tag.getName()} + <i class="close material-icons">close</i> + </div> + </#list> + </#if> + </div> + </div> + </div> <br> <div class="row hide-on-small-only"> <div class="col m6 l4 offset-l2 right-align"> @@ -109,7 +125,19 @@ <!-- Pass localization to JS --> <#import "../datePicker.ftl" as datePicker> <@datePicker.datePickerLocalization/> - <script>amountValidationMessage = "${locale.getString("warning.payment.amount")}";</script> + <script> + amountValidationMessage = "${locale.getString("warning.payment.amount")}"; + tagsPlaceholder = "${locale.getString("tagfield.placeholder")}"; + </script> + + <!-- Tag autocomplete --> + <script> + tagAutoComplete = { + <#list helpers.getAllTags() as tag> + '${tag.getName()}': null, + </#list> + } + </script> <!-- Scripts--> <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>