diff --git a/src/main/java/de/deadlocker8/budgetmaster/search/Search.java b/src/main/java/de/deadlocker8/budgetmaster/search/Search.java new file mode 100644 index 0000000000000000000000000000000000000000..942f4c308de689043180fb74688a628305789303 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/search/Search.java @@ -0,0 +1,85 @@ +package de.deadlocker8.budgetmaster.search; + +public class Search +{ + private String searchText; + private boolean searchName; + private boolean searchDescription; + private boolean searchCategory; + private boolean searchTags; + + public Search() + { + } + + public Search(String searchText, boolean searchName, boolean searchDescription, boolean searchCategory, boolean searchTags) + { + this.searchText = searchText; + this.searchName = searchName; + this.searchDescription = searchDescription; + this.searchCategory = searchCategory; + this.searchTags = searchTags; + } + + public String getSearchText() + { + return searchText; + } + + public void setSearchText(String searchText) + { + this.searchText = searchText; + } + + public boolean isSearchName() + { + return searchName; + } + + public void setSearchName(boolean searchName) + { + this.searchName = searchName; + } + + public boolean isSearchDescription() + { + return searchDescription; + } + + public void setSearchDescription(boolean searchDescription) + { + this.searchDescription = searchDescription; + } + + public boolean isSearchCategory() + { + return searchCategory; + } + + public void setSearchCategory(boolean searchCatgeory) + { + this.searchCategory = searchCatgeory; + } + + public boolean isSearchTags() + { + return searchTags; + } + + public void setSearchTags(boolean searchTags) + { + this.searchTags = searchTags; + } + + @Override + public String toString() + { + return "Search{" + + "searchText='" + searchText + '\'' + + ", searchName=" + searchName + + ", searchDescription=" + searchDescription + + ", searchCategory=" + searchCategory + + ", searchTags=" + searchTags + + '}'; + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/search/SearchController.java b/src/main/java/de/deadlocker8/budgetmaster/search/SearchController.java index 67967c172493da7329e5f864ee95643ecfcfe6ae..4a398c822795f579fa3290be2dfff520314a4fec 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/search/SearchController.java +++ b/src/main/java/de/deadlocker8/budgetmaster/search/SearchController.java @@ -8,6 +8,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -26,13 +28,14 @@ public class SearchController extends BaseController this.transactionService = transactionService; } - @RequestMapping(value = "/search", method = RequestMethod.GET) - public String search(Model model, @RequestParam(defaultValue = "") String searchText) + @RequestMapping(value = "/search", method = RequestMethod.POST) + public String search(Model model, @ModelAttribute("NewSearch") Search search) { - Specification<Transaction> specification = TransactionSearchSpecifications.withDynamicQuery(searchText); + System.out.println(search); + Specification<Transaction> specification = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> transactions = transactionService.getRepository().findAll(specification); model.addAttribute("transactions", transactions); - model.addAttribute("searchText", searchText); + model.addAttribute("search", search); return "search/search"; } } \ No newline at end of file diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java index b3d37357a9af265b1fffb9db99ce38e9ce95d355..377f6c1aa0684bfe3d08e4e20635a0ec372f49ae 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java +++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java @@ -1,10 +1,7 @@ package de.deadlocker8.budgetmaster.transactions; import de.deadlocker8.budgetmaster.categories.Category; -import de.deadlocker8.budgetmaster.tags.Tag; -import de.deadlocker8.budgetmaster.tags.TagService; -import de.deadlocker8.budgetmaster.tags.Tag_; -import org.springframework.beans.factory.annotation.Autowired; +import de.deadlocker8.budgetmaster.search.Search; import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.Join; @@ -15,20 +12,34 @@ import java.util.List; public class TransactionSearchSpecifications { - public static Specification<Transaction> withDynamicQuery(final String searchText) + public static Specification<Transaction> withDynamicQuery(final Search search) { return (transaction, query, builder) -> { - final String pattern = "%" + searchText.toLowerCase() + "%"; + final String pattern = "%" + search.getSearchText().toLowerCase() + "%"; List<Predicate> predicates = new ArrayList<>(); - predicates.add(builder.like(builder.lower(transaction.get(Transaction_.name)), pattern)); - predicates.add(builder.like(builder.lower(transaction.get(Transaction_.description)), pattern)); - Join<Transaction, Category> categoryJoin = transaction.join(Transaction_.category, JoinType.INNER); - predicates.add(builder.like(builder.lower(categoryJoin.get("name").as(String.class)), pattern)); + if(search.isSearchName()) + { + predicates.add(builder.like(builder.lower(transaction.get(Transaction_.name)), pattern)); + } -// Join<Transaction, Tag> tagJoin = transaction.join(Transaction_.tags, JoinType.INNER); -// predicates.add(builder.like(builder.lower(tagJoin.get(Tag_.name)), pattern)); + if(search.isSearchDescription()) + { + predicates.add(builder.like(builder.lower(transaction.get(Transaction_.description)), pattern)); + } + + if(search.isSearchCategory()) + { + Join<Transaction, Category> categoryJoin = transaction.join(Transaction_.category, JoinType.INNER); + predicates.add(builder.like(builder.lower(categoryJoin.get("name").as(String.class)), pattern)); + } + +// if(search.isSearchTags()) +// { +// Join<Transaction, Tag> tagJoin = transaction.join(Transaction_.tags, JoinType.INNER); +// predicates.add(builder.like(builder.lower(tagJoin.get(Tag_.name)), pattern)); +// } query.orderBy(builder.desc(transaction.get(Transaction_.date))); Predicate[] predicatesArray = new Predicate[predicates.size()]; diff --git a/src/main/resources/languages/_de.properties b/src/main/resources/languages/_de.properties index 3662b61d0de89272b949098867e658f359158323..8b81644ef2fb90b311f797655b407a2b35a3367a 100644 --- a/src/main/resources/languages/_de.properties +++ b/src/main/resources/languages/_de.properties @@ -208,7 +208,12 @@ datepicker.label.month=Monat: datepicker.label.year=Jahr: datepicker.button.confirm=�bernehmen -search.for=F�r +search=Suche +search.submit=Suchen +search.in.name=Name +search.in.description=Beschreibung +search.in.category=Kategorie +search.in.tags=Tags # ABOUT about=�ber {0} diff --git a/src/main/resources/languages/_en.properties b/src/main/resources/languages/_en.properties index d90a8f65b7341dda6e040612cb8f1724bd36e138..e36bb7fab9ea8aa0e26aeb9390271353b7652a95 100644 --- a/src/main/resources/languages/_en.properties +++ b/src/main/resources/languages/_en.properties @@ -209,7 +209,12 @@ datepicker.label.month=Month: datepicker.label.year=Year: datepicker.button.confirm=Apply -search.for=For +search=Search +search.submit=Search +search.in.name=Name +search.in.description=Description +search.in.category=Category +search.in.tags=Tags # ABOUT about=About {0} diff --git a/src/main/resources/static/css/dark/transactions.css b/src/main/resources/static/css/dark/transactions.css index f8c759c0d4b03d6ac14f96f3ba71c8f66b1a3e2c..6b7b37a92484f069f5929d9ff020341d72b0d55b 100644 --- a/src/main/resources/static/css/dark/transactions.css +++ b/src/main/resources/static/css/dark/transactions.css @@ -24,7 +24,9 @@ } .transaction-container .category-circle, -.transaction-container .category-circle-small { +.transaction-container .category-circle-small, +.search-container .category-circle, +.search-container .category-circle-small { margin: auto; } diff --git a/src/main/resources/static/css/transactions.css b/src/main/resources/static/css/transactions.css index f8c759c0d4b03d6ac14f96f3ba71c8f66b1a3e2c..6b7b37a92484f069f5929d9ff020341d72b0d55b 100644 --- a/src/main/resources/static/css/transactions.css +++ b/src/main/resources/static/css/transactions.css @@ -24,7 +24,9 @@ } .transaction-container .category-circle, -.transaction-container .category-circle-small { +.transaction-container .category-circle-small, +.search-container .category-circle, +.search-container .category-circle-small { margin: auto; } diff --git a/src/main/resources/templates/helpers/navbar.ftl b/src/main/resources/templates/helpers/navbar.ftl index 785f8242685b101c10edbff582c579b8b50c21b3..d4589c62a98c7577433546796f4a4448bc22b428 100644 --- a/src/main/resources/templates/helpers/navbar.ftl +++ b/src/main/resources/templates/helpers/navbar.ftl @@ -64,7 +64,13 @@ <#macro itemSearch> <nav class="searchWrapper"> <div class="nav-wrapper"> - <form name="NewSearch" action="<@s.url '/search'/>" method="get"> + <form name="NewSearch" action="<@s.url '/search'/>" method="post"> + <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> + <input type="hidden" name="searchName" value="true"> + <input type="hidden" name="searchDescription" value="true"> + <input type="hidden" name="searchCategory" value="true"> + <input type="hidden" name="searchTags" value="true"> + <div class="input-field"> <input id="search" class="text-color" name="searchText" type="search"> <label class="label-icon" for="search"><i class="material-icons">search</i></label> diff --git a/src/main/resources/templates/search/search.ftl b/src/main/resources/templates/search/search.ftl index 0eb01f8189f367d62b6c2a3e4bac5a1d48b4ca66..675ddf1e5a1112d66bfdb984e25af8e3ec8408c3 100644 --- a/src/main/resources/templates/search/search.ftl +++ b/src/main/resources/templates/search/search.ftl @@ -20,45 +20,90 @@ <div class="headline">${locale.getString("menu.search.results")}</div> </div> </div> - <div class="container"> + <form name="NewSearch" action="<@s.url '/search'/>" method="post"> + <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> + <div class="row no-margin-bottom valign-wrapper"> + <div class="col s10 m7 offset-m1 l6 offset-l2"> + <div class="input-field"> + <input id="searchText" type="text" name="searchText" value="${search.getSearchText()}"> + <label for="searchText">${locale.getString("search")}</label> + </div> + </div> + + <div class="col s2 m3 l4"> + <div class="hide-on-small-only"> + <button class="btn waves-effect waves-light budgetmaster-blue" type="submit" name="action"> + <i class="material-icons left">search</i>${locale.getString("search.submit")} + </button> + </div> + <div class="hide-on-med-and-up"> + <button class="btn waves-effect waves-light budgetmaster-blue" type="submit" name="action"> + <i class="material-icons">search</i> + </button> + </div> + </div> + </div> + <div class="row"> - <div class="col s12"> - <div class="headline center-align">${locale.getString("search.for")} "${searchText}"</div> + <div class="col s8 offset-s2 m4 offset-m2 l3 offset-l3"> + <label> + <input type="checkbox" name="searchName" <#if search.isSearchName()>checked="checked"</#if>> + <span class="text-color">${locale.getString('search.in.name')}</span> + </label> + </div> + <div class="col s8 offset-s2 m6 l6"> + <label> + <input type="checkbox" name="searchDescription" <#if search.isSearchDescription()>checked="checked"</#if>> + <span class="text-color">${locale.getString('search.in.description')}</span> + </label> + </div> + <div class="col s8 offset-s2 m4 offset-m2 l3 offset-l3"> + <label> + <input type="checkbox" name="searchCategory" <#if search.isSearchCategory()>checked="checked"</#if>> + <span class="text-color">${locale.getString('search.in.category')}</span> + </label> + </div> + <div class="col s8 offset-s2 m6 l6"> + <label> + <input type="checkbox" name="searchTags" <#if search.isSearchTags()>checked="checked"</#if>> + <span class="text-color">${locale.getString('search.in.tags')}</span> + </label> </div> </div> - <div class="row transaction-container"> - <div class="col s12"> - <#list transactions as transaction> - <div class="card-panel search-result"> - <div class="hide-on-large-only"> - <div class="row valign-wrapper"> - <div class="col s3 center-align bold transaction-text"> - ${dateService.getDateStringWithoutYear(transaction.date)} - </div> - <@transactionsMacros.transactionType transaction/> - <@transactionsMacros.transactionLinks transaction/> - </div> - <div class="row valign-wrapper no-margin-bottom"> - <@transactionsMacros.transactionCategory transaction "center-align"/> - <@transactionsMacros.transactionNameAndDescription transaction/> - <@transactionsMacros.transactionAmount transaction transaction.getAccount()/> + </form> + + <div class="row search-container"> + <div class="col s12"> + <#list transactions as transaction> + <div class="card-panel search-result"> + <div class="hide-on-large-only"> + <div class="row valign-wrapper"> + <div class="col s3 center-align bold transaction-text"> + ${dateService.getDateStringWithoutYear(transaction.date)} </div> + <@transactionsMacros.transactionType transaction/> + <@transactionsMacros.transactionLinks transaction/> </div> - <div class="hide-on-med-and-down"> - <div class="row valign-wrapper no-margin-bottom"> - <div class="col l1 xl1 bold transaction-text transaction-line-height"> - ${dateService.getDateStringWithoutYear(transaction.date)} - </div> - <@transactionsMacros.transactionCategory transaction "left-align"/> - <@transactionsMacros.transactionType transaction/> - <@transactionsMacros.transactionNameAndDescription transaction/> - <@transactionsMacros.transactionAmount transaction transaction.getAccount()/> - <@transactionsMacros.transactionLinks transaction/> + <div class="row valign-wrapper no-margin-bottom"> + <@transactionsMacros.transactionCategory transaction "center-align"/> + <@transactionsMacros.transactionNameAndDescription transaction/> + <@transactionsMacros.transactionAmount transaction transaction.getAccount()/> + </div> + </div> + <div class="hide-on-med-and-down"> + <div class="row valign-wrapper no-margin-bottom"> + <div class="col l1 xl1 bold transaction-text transaction-line-height"> + ${dateService.getDateStringWithoutYear(transaction.date)} </div> + <@transactionsMacros.transactionCategory transaction "left-align"/> + <@transactionsMacros.transactionType transaction/> + <@transactionsMacros.transactionNameAndDescription transaction/> + <@transactionsMacros.transactionAmount transaction transaction.getAccount()/> + <@transactionsMacros.transactionLinks transaction/> </div> </div> - </#list> - </div> + </div> + </#list> </div> </div> </div> diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java index 4d96dc189b4eab70439ac6d6ef842d75f8f1298a..8a021f882b0f6168d7995d93d3a9ca907bb1d44d 100644 --- a/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java @@ -10,6 +10,7 @@ import de.deadlocker8.budgetmaster.repeating.RepeatingOption; import de.deadlocker8.budgetmaster.repeating.RepeatingOptionRepository; import de.deadlocker8.budgetmaster.repeating.endoption.RepeatingEndAfterXTimes; import de.deadlocker8.budgetmaster.repeating.modifier.RepeatingModifierDays; +import de.deadlocker8.budgetmaster.search.Search; import de.deadlocker8.budgetmaster.tags.Tag; import de.deadlocker8.budgetmaster.tags.TagRepository; import de.deadlocker8.budgetmaster.transactions.Transaction; @@ -126,7 +127,8 @@ public class TransactionSearchSpecificationsTest @Test public void getMatches_OnlyName() { - Specification spec = TransactionSearchSpecifications.withDynamicQuery("Test"); + Search search = new Search("Test", true, false, false, false); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> results = transactionRepository.findAll(spec); assertTrue(results.contains(transaction1)); @@ -138,7 +140,8 @@ public class TransactionSearchSpecificationsTest @Test public void getMatches_PartialName() { - Specification spec = TransactionSearchSpecifications.withDynamicQuery("es"); + Search search = new Search("es", true, false, false, false); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> results = transactionRepository.findAll(spec); assertTrue(results.contains(transaction1)); @@ -150,7 +153,8 @@ public class TransactionSearchSpecificationsTest @Test public void getMatches_IgnoreCase() { - Specification spec = TransactionSearchSpecifications.withDynamicQuery("tEST"); + Search search = new Search("tEST", true, true, true, true); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> results = transactionRepository.findAll(spec); assertTrue(results.contains(transaction1)); @@ -162,7 +166,8 @@ public class TransactionSearchSpecificationsTest @Test public void getMatches_OnlyDescription() { - Specification spec = TransactionSearchSpecifications.withDynamicQuery("What"); + Search search = new Search("What", true, true, true, true); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> results = transactionRepository.findAll(spec); assertTrue(results.contains(transaction1)); @@ -174,7 +179,8 @@ public class TransactionSearchSpecificationsTest @Test public void getMatches_OnlyCategory() { - Specification spec = TransactionSearchSpecifications.withDynamicQuery(category2.getName()); + Search search = new Search(category2.getName(), false, false, true, false); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> results = transactionRepository.findAll(spec); assertFalse(results.contains(transaction1)); @@ -186,7 +192,8 @@ public class TransactionSearchSpecificationsTest @Test public void getMatches_Order() { - Specification spec = TransactionSearchSpecifications.withDynamicQuery(""); + Search search = new Search("", true, true, true, true); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> results = transactionRepository.findAll(spec); List<Transaction> expected = new ArrayList<>(); @@ -201,7 +208,8 @@ public class TransactionSearchSpecificationsTest @Test public void getMatches_Mixed() { - Specification spec = TransactionSearchSpecifications.withDynamicQuery("e"); + Search search = new Search("e", true, true, true, true); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> results = transactionRepository.findAll(spec); assertTrue(results.contains(transaction1)); @@ -213,7 +221,18 @@ public class TransactionSearchSpecificationsTest @Test public void getMatches_NoMatches() { - Specification spec = TransactionSearchSpecifications.withDynamicQuery("asuzgdzasuiduzasds"); + Search search = new Search("asuzgdzasuiduzasds", true, true, true,true); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); + + List<Transaction> results = transactionRepository.findAll(spec); + assertEquals(0, results.size()); + } + + @Test + public void getMatches_SearchNothing() + { + Search search = new Search("egal", false, false, false,false); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); List<Transaction> results = transactionRepository.findAll(spec); assertEquals(0, results.size());