diff options
| author | realtradam <[email protected]> | 2024-07-11 23:20:40 -0400 |
|---|---|---|
| committer | realtradam <[email protected]> | 2024-07-11 23:20:40 -0400 |
| commit | d0e45a9093b33d4e5cb5f57fabdcb807dc8e8ff0 (patch) | |
| tree | c5e4457bdbc64d5ecbe72e43258d2c7af70063eb | |
| parent | 40075162def7d0a4ce9d795f4cfe83264fac50b4 (diff) | |
| download | spring-blog-d0e45a9093b33d4e5cb5f57fabdcb807dc8e8ff0.tar.gz spring-blog-d0e45a9093b33d4e5cb5f57fabdcb807dc8e8ff0.zip | |
add search functionality
7 files changed, 145 insertions, 8 deletions
diff --git a/src/main/java/com/blog/web/controllers/ArticleController.java b/src/main/java/com/blog/web/controllers/ArticleController.java index 0e58877..0cdff15 100644 --- a/src/main/java/com/blog/web/controllers/ArticleController.java +++ b/src/main/java/com/blog/web/controllers/ArticleController.java @@ -7,10 +7,7 @@ import jakarta.validation.Valid; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; import java.util.List; @@ -30,6 +27,13 @@ public class ArticleController { return "index"; } + @GetMapping("/articles/{articleId}") + public String showArticle(@PathVariable("articleId") long articleId, Model model) { + ArticleDto articleDto = articleService.findArticleById(articleId); + model.addAttribute("article", articleDto); + return "articles/show"; + } + @GetMapping("/articles/new") public String createArticleForm(Model model) { Article article = new Article(); @@ -49,6 +53,12 @@ public class ArticleController { return "redirect:/articles"; } + @GetMapping("/articles/delete/{articleId}") + public String deleteArticle(@PathVariable("articleId") Long articleId) { + articleService.delete(articleId); + return "redirect:/articles"; + } + @GetMapping("/articles/edit/{articleId}") public String editArticleForm(@PathVariable("articleId") long articleId, Model model) { ArticleDto articleDto = articleService.findArticleById(articleId); @@ -68,6 +78,13 @@ public class ArticleController { return "redirect:/articles"; } + @GetMapping("/articles/search") + public String searchArticle(@RequestParam(value = "search") String search, Model model) { + List<ArticleDto> articles = articleService.searchArticles(search); + model.addAttribute("articles", articles); + return "index"; + } + @GetMapping("/articles") public String getArticles() { return "redirect:/"; diff --git a/src/main/java/com/blog/web/repository/ArticleRepository.java b/src/main/java/com/blog/web/repository/ArticleRepository.java index c7e8c04..db9edf6 100644 --- a/src/main/java/com/blog/web/repository/ArticleRepository.java +++ b/src/main/java/com/blog/web/repository/ArticleRepository.java @@ -2,7 +2,13 @@ package com.blog.web.repository; import com.blog.web.models.Article; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.Optional; public interface ArticleRepository extends JpaRepository<Article, Long> { - + Optional<Article> findByTitle(String url); + @Query("SELECT a from Article a WHERE a.title LIKE CONCAT('%', :search, '%')") + List<Article> searchArticles(String search); } diff --git a/src/main/java/com/blog/web/services/ArticleService.java b/src/main/java/com/blog/web/services/ArticleService.java index e2ec309..9cab70b 100644 --- a/src/main/java/com/blog/web/services/ArticleService.java +++ b/src/main/java/com/blog/web/services/ArticleService.java @@ -13,4 +13,8 @@ public interface ArticleService { ArticleDto findArticleById(long articleId); void updateArticle(ArticleDto articleDto); + + void delete(Long articleId); + + List<ArticleDto> searchArticles(String search); } diff --git a/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java b/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java index b9e11f6..c8e2903 100644 --- a/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java +++ b/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java @@ -41,6 +41,17 @@ public class ArticleServiceImpl implements ArticleService { articleRepository.save(article); } + @Override + public void delete(Long articleId) { + articleRepository.deleteById(articleId); + } + + @Override + public List<ArticleDto> searchArticles(String search) { + List<Article> articles = articleRepository.searchArticles(search); + return articles.stream().map(article -> mapToArticleDto(article)).collect(Collectors.toList()); + } + private Article mapToArticle(ArticleDto articleDto) { Article article = Article.builder() .id(articleDto.getId()) diff --git a/src/main/resources/templates/articles/show.html b/src/main/resources/templates/articles/show.html new file mode 100644 index 0000000..206aaae --- /dev/null +++ b/src/main/resources/templates/articles/show.html @@ -0,0 +1,87 @@ +<!DOCTYPE html> +<html lang="en" + xmlns:th="http://www.thymeleaf.org" + xmlns:layout="https://www.ultraq.net.nz/thymeleaf/layout" + layout:decorate="~{layout}" +> +<body layout:fragment="content"> + + + +<!--Title--> +<div class="text-center pt-16 md:pt-32"> + <p th:text="${article.createdOn}" class="text-sm md:text-base text-green-500 font-bold"></p> + <h1 th:text="${article.title}" class="font-bold break-normal text-3xl md:text-5xl">Welcome to Ghostwind CSS</h1> +</div> + +<!--image--> +<div class="container w-full max-w-6xl mx-auto bg-white bg-cover mt-8 rounded" th:style="'background:url(' + @{${article.photoUrl}} + '); height: 75vh;'"></div> + +<!--Container--> +<div class="container max-w-5xl mx-auto -mt-32"> + + <div class="mx-0 sm:mx-6"> + + <div class="bg-white w-full p-8 md:p-24 text-xl md:text-2xl text-gray-800 leading-normal" style="font-family:Georgia,serif;"> + + <!--Post Content--> + + <div th:text="${article.content}"></div> + + <!--/ Post Content--> + + </div> + + + <!--Subscribe--> + <div class="container font-sans bg-green-100 rounded mt-8 p-4 md:p-24 text-center"> + <h2 class="font-bold break-normal text-2xl md:text-4xl">Subscribe to Ghostwind CSS</h2> + <h3 class="font-bold break-normal font-normal text-gray-600 text-base md:text-xl">Get the latest posts delivered right to your inbox</h3> + <div class="w-full text-center pt-4"> + <form action="#"> + <div class="max-w-sm mx-auto p-1 pr-0 flex flex-wrap items-center"> + <input type="email" placeholder="[email protected]" class="flex-1 appearance-none rounded shadow p-3 text-gray-600 mr-2 focus:outline-none"> + <button type="submit" class="flex-1 mt-4 md:mt-0 block md:inline-block appearance-none bg-green-500 text-white text-base font-semibold tracking-wider uppercase py-4 rounded shadow hover:bg-green-400">Subscribe</button> + </div> + </form> + </div> + </div> + <!-- /Subscribe--> + + + <!--Author--> + <div class="flex w-full items-center font-sans p-8 md:p-24"> + <img class="w-10 h-10 rounded-full mr-4" src="http://i.pravatar.cc/300" alt="Avatar of Author"> + <div class="flex-1"> + <p class="text-base font-bold text-base md:text-xl leading-none">Ghostwind CSS</p> + <p class="text-gray-600 text-xs md:text-base">Tailwind CSS version of Ghost's Casper theme by <a class="text-gray-800 hover:text-green-500 no-underline border-b-2 border-green-500" href="https://www.tailwindtoolbox.com">TailwindToolbox.com</a></p> + </div> + <div class="justify-end"> + + </div> + <!--/Author--> + + </div> + + + </div> + +</div> + + +<!-- Scroll Top Button --> +<button class="btn-toggle-round scroll-top js-scroll-top" type="button" title="Scroll to top"> + <svg class="progress-circle" width="100%" height="100%" viewBox="-1 -1 102 102"> + <path d="M50,1 a49,49 0 0,1 0,98 a49,49 0 0,1 0,-98"/> + </svg> + <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-up" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="cuurentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> + <path stroke="none" d="M0 0h24v24H0z" fill="none"/> + <line x1="12" y1="5" x2="12" y2="19" /> + <line x1="18" y1="11" x2="12" y2="5" /> + <line x1="6" y1="11" x2="12" y2="5" /> + </svg> +</button> + + +</body> +</html>
\ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index ed98605..1dcef48 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -103,12 +103,13 @@ <!--1/2 col --> <div th:each="article :${articles}" class="w-full md:w-1/2 p-6 flex flex-col flex-grow flex-shrink"> <div class="flex-1 bg-white rounded-t rounded-b-none overflow-hidden shadow-lg"> - <a href="#" class="flex flex-wrap no-underline hover:no-underline"> + <a th:href="@{/articles/{articleId}(articleId=${article.id})}" class="flex flex-wrap no-underline hover:no-underline"> <img th:src="${article.photoUrl}" class="h-full w-full rounded-t pb-6"> <div th:text="${article.title}" class="w-full font-bold text-xl text-gray-900 px-6">Lorem ipsum dolor sit amet.</div> - <p th:text="${article.content}" class="text-gray-800 font-serif text-base px-6 mb-5"></p> + <p class="text-gray-800 font-serif text-base px-6 mb-5"></p> </a> <a th:href="@{/articles/edit/{articleId}(articleId=${article.id})}" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 ml-4 text-sm rounded">Edit</a> + <a th:href="@{/articles/delete/{articleId}(articleId=${article.id})}" class="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 ml-4 text-sm rounded">Delete</a> </div> <div class="flex-none mt-auto bg-white rounded-b rounded-t-none overflow-hidden shadow-lg p-6"> <div class="flex items-center justify-between"> diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html index 407ecd9..17d6939 100644 --- a/src/main/resources/templates/layout.html +++ b/src/main/resources/templates/layout.html @@ -33,7 +33,7 @@ 👻 <span class="hidden w-0 md:w-auto md:block pl-1">Ghostwind CSS</span> </a> </div> - <div class="flex pl-4 text-sm"> + <div class="flex pl-4 text-sm place-content-between w-full"> <ul class="list-reset flex justify-between flex-1 md:flex-none items-center"> <li class="mr-2"> <a class="inline-block py-2 px-2 text-white no-underline" href="/">HOME</a> @@ -48,6 +48,17 @@ <a class="inline-block text-indigo-200 no-underline hover:text-indigo-100 hover:text-underline py-2 px-2" href="#">LINK</a> </li> </ul> + <form th:action="@{/articles/search}" class="w-full max-w-md"> + <div class="flex flex-wrap -mx-3"> + <div class="w-full px-3"> + <input class="appearance-none block w-full bg-gray-200 text-gray-700 border rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white" + id="search" + type="search" + name="search" + placeholder="Search"> + </div> + </div> + </form> </div> </div> </nav> |
