summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorrealtradam <[email protected]>2024-07-24 23:15:03 -0400
committerrealtradam <[email protected]>2024-07-24 23:15:03 -0400
commit054e25bb66269d1d99ee0b0afa3b26abee2db80f (patch)
tree0333ca0d25a9c435addd3e0e99518dd578f814d4
parente36832834564f401622ef293c19fc778ab43f9ae (diff)
downloadspring-blog-054e25bb66269d1d99ee0b0afa3b26abee2db80f.tar.gz
spring-blog-054e25bb66269d1d99ee0b0afa3b26abee2db80f.zip
port api endpoints to json and add react pages to interact with them
-rw-r--r--backend/src/main/java/com/blog/web/controllers/ArticleController.java29
-rw-r--r--backend/src/main/java/com/blog/web/repository/ArticleRepository.java4
-rw-r--r--backend/src/main/java/com/blog/web/services/ArticleService.java7
-rw-r--r--backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java21
-rw-r--r--frontend/index.html6
-rw-r--r--frontend/public/abstract-polygonal-banner-background-vector.jpgbin0 -> 1841814 bytes
-rw-r--r--frontend/src/components/Layout.tsx132
-rw-r--r--frontend/src/pages/Article.tsx25
-rw-r--r--frontend/src/pages/Home.tsx111
-rw-r--r--frontend/src/pages/Register.tsx55
-rw-r--r--frontend/src/routes/index.tsx25
11 files changed, 354 insertions, 61 deletions
diff --git a/backend/src/main/java/com/blog/web/controllers/ArticleController.java b/backend/src/main/java/com/blog/web/controllers/ArticleController.java
index df9d40c..6bd1abe 100644
--- a/backend/src/main/java/com/blog/web/controllers/ArticleController.java
+++ b/backend/src/main/java/com/blog/web/controllers/ArticleController.java
@@ -16,6 +16,7 @@ import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
+@RequestMapping("/api/v1")
@RestController
@Controller
public class ArticleController {
@@ -51,21 +52,23 @@ public class ArticleController {
return articles;
}
- @GetMapping("/articles/{articleId}")
- public String showArticle(@PathVariable("articleId") long articleId, Model model) {
- ArticleDto articleDto = articleService.findArticleById(articleId);
- model.addAttribute("article", articleDto);
- UserEntity user = userService.getLoggedInUser().orElse(new UserEntity());
- model.addAttribute("user", user);
- return "articles/show";
+ @CrossOrigin
+ @GetMapping("/article/{articleId}")
+ public ArticlePublicDto showArticle(@PathVariable("articleId") long articleId, Model model) {
+ ArticlePublicDto articlePublicDto = articleService.findArticlePublicById(articleId);
+ //model.addAttribute("article", articlePublicDto);
+ //UserEntity user = userService.getLoggedInUser().orElse(new UserEntity());
+ //model.addAttribute("user", user);
+ //return "articles/show";
+ return articlePublicDto;
}
- @GetMapping("/articles/new")
+ /*@GetMapping("/articles/new")
public String createArticleForm(Model model) {
model.addAttribute("user", userService.getLoggedInUser().orElse(new UserEntity()));
model.addAttribute("article", new Article());
return "articles/new";
- }
+ }*/
@PostMapping("/articles/new")
public String saveArticle(@Valid @ModelAttribute("article") ArticleDto articleDto, BindingResult result, Model model) {
@@ -112,10 +115,10 @@ public class ArticleController {
@GetMapping("/articles/search")
public String searchArticle(@RequestParam(value = "search") String search, Model model) {
- UserEntity user = userService.getLoggedInUser().orElse(new UserEntity());
- model.addAttribute("user", user);
- List<ArticleDto> articles = articleService.searchArticles(search);
- model.addAttribute("articles", articles);
+ //UserEntity user = userService.getLoggedInUser().orElse(new UserEntity());
+ //model.addAttribute("user", user);
+ HashSet<ArticlePublicDto> articles = articleService.searchPublicArticles(search);
+ //model.addAttribute("articles", articles);
return "index";
}
diff --git a/backend/src/main/java/com/blog/web/repository/ArticleRepository.java b/backend/src/main/java/com/blog/web/repository/ArticleRepository.java
index 594cb15..f40eada 100644
--- a/backend/src/main/java/com/blog/web/repository/ArticleRepository.java
+++ b/backend/src/main/java/com/blog/web/repository/ArticleRepository.java
@@ -4,9 +4,9 @@ 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.HashSet;
public interface ArticleRepository extends JpaRepository<Article, Long> {
@Query("SELECT a from Article a WHERE a.title LIKE CONCAT('%', :search, '%')")
- List<Article> searchArticles(String search);
+ HashSet<Article> searchArticles(String search);
}
diff --git a/backend/src/main/java/com/blog/web/services/ArticleService.java b/backend/src/main/java/com/blog/web/services/ArticleService.java
index 1bfe38f..1e50df0 100644
--- a/backend/src/main/java/com/blog/web/services/ArticleService.java
+++ b/backend/src/main/java/com/blog/web/services/ArticleService.java
@@ -4,6 +4,7 @@ import com.blog.web.dto.ArticleDto;
import com.blog.web.dto.ArticlePublicDto;
import com.blog.web.models.Article;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -18,5 +19,9 @@ public interface ArticleService {
boolean delete(Long articleId);
- List<ArticleDto> searchArticles(String search);
+ //List<ArticleDto> searchArticles(String search);
+
+ ArticlePublicDto findArticlePublicById(long articleId);
+
+ HashSet<ArticlePublicDto> searchPublicArticles(String search);
}
diff --git a/backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java b/backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java
index 7073073..993d798 100644
--- a/backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java
+++ b/backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java
@@ -11,14 +11,14 @@ import com.blog.web.services.ArticleService;
import com.blog.web.services.UserService;
import org.springframework.stereotype.Service;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.blog.web.mappers.ArticleMapper;
-import static com.blog.web.mappers.ArticleMapper.mapToArticle;
-import static com.blog.web.mappers.ArticleMapper.mapToArticleDto;
+import static com.blog.web.mappers.ArticleMapper.*;
@Service
public class ArticleServiceImpl implements ArticleService {
@@ -86,9 +86,20 @@ public class ArticleServiceImpl implements ArticleService {
}
}
+ //@Override
+ //public List<ArticleDto> searchArticles(String search) {
+ // List<Article> articles = articleRepository.searchArticles(search);
+ // return articles.stream().map(article -> mapToArticleDto(article)).collect(Collectors.toList());
+ //}
+
+ @Override
+ public ArticlePublicDto findArticlePublicById(long articleId) {
+ return new ArticlePublicDto(articleRepository.findById(articleId).get());
+ }
+
@Override
- public List<ArticleDto> searchArticles(String search) {
- List<Article> articles = articleRepository.searchArticles(search);
- return articles.stream().map(article -> mapToArticleDto(article)).collect(Collectors.toList());
+ public HashSet<ArticlePublicDto> searchPublicArticles(String search) {
+ HashSet<Article> articles = articleRepository.searchArticles(search);
+ return articles.stream().map(article -> mapToArticlePublicDto(article)).collect(Collectors.toCollection(HashSet::new));
}
}
diff --git a/frontend/index.html b/frontend/index.html
index e4b78ea..82a3d13 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
- <body>
- <div id="root"></div>
+ <body class="w-full">
+ <div id="root" class="w-full"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
-</html>
+</div>
diff --git a/frontend/public/abstract-polygonal-banner-background-vector.jpg b/frontend/public/abstract-polygonal-banner-background-vector.jpg
new file mode 100644
index 0000000..1c6746e
--- /dev/null
+++ b/frontend/public/abstract-polygonal-banner-background-vector.jpg
Binary files differ
diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx
index f58f1fc..f7b8591 100644
--- a/frontend/src/components/Layout.tsx
+++ b/frontend/src/components/Layout.tsx
@@ -5,12 +5,132 @@ export default function Layout()
{
return(
<>
- <div className="w-screen h-screen flex border-none">
- <h1>Spring Blog</h1>
- <div className="w-full">
- <Outlet/>
- </div>
- </div>
+<div className="w-full bg-gray-200 min-h-screen font-sans leading-normal tracking-normal">
+
+<nav className="bg-gray-900 p-4 mt-0 w-full">
+ <div className="container mx-auto flex items-center">
+ <div className="flex text-white font-extrabold">
+{/*th:if="${(user == null || user.username == null)}"*/}
+ <a className="flex text-white text-base no-underline hover:text-white hover:no-underline" href="#">
+ ☕<span className="hidden w-0 md:w-auto md:block pl-1">Spring!</span>
+ </a>
+{/*th:if="${!(user == null || user.username == null)}"*/}
+ <a className="flex text-white text-base no-underline hover:text-white hover:no-underline" href="#">
+{/*th:text="'Logged in as: ' + ${user.username}"*/}
+ ☕<span className="hidden w-0 md:w-auto md:block pl-1"></span>
+ </a>
+ </div>
+ <div className="flex pl-4 text-sm place-content-between w-full">
+ <ul className="list-reset flex justify-between flex-1 md:flex-none items-center">
+ <li className="mr-2">
+ <a className="inline-block py-2 px-2 text-white no-underline" href="/">HOME</a>
+ </li>
+ <li className="mr-2">
+ <a className="inline-block text-indigo-200 no-underline hover:text-gray-100 hover:text-underline py-2 px-2" href="/articles/new">NEW</a>
+ </li>
+ <li className="mr-2">
+ <a className="inline-block text-indigo-200 no-underline hover:text-indigo-100 hover:text-underline py-2 px-2" href="/register">REGISTER</a>
+ </li>
+ <li className="mr-2">
+{/*th:if="${(user == null || user.username == null)}"*/}
+ <a className="inline-block text-indigo-200 no-underline hover:text-indigo-100 hover:text-underline py-2 px-2" href="/userlogin">LOGIN</a>
+{/*th:if="${!(user == null || user.username == null)}"*/}
+ <a className="inline-block text-indigo-200 no-underline hover:text-indigo-100 hover:text-underline py-2 px-2" href="/logout">LOGOUT</a>
+ </li>
+ </ul>
+ {/*th:action="@{/articles/search}"*/}
+ <form className="w-full max-w-md">
+ <div className="flex flex-wrap -mx-3">
+ <div className="w-full px-3">
+ <input className="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>
+
+{/*slide in nav*/}
+<div id="header" className="bg-white fixed w-full z-10 top-0 hidden animated" style={{opacity: '.95'}}>
+ <div className="bg-white">
+ <div className="flex flex-wrap items-center content-center">
+ <div className="flex w-1/2 justify-start text-white font-extrabold">
+ <a className="flex text-gray-100 no-underline hover:text-gray-900 hover:no-underline pl-2" href="#">
+ 👻 <span className="hidden w-0 md:w-auto md:block pl-1">Ghostwind CSS</span>
+ </a>
+ </div>
+ <div className="flex w-1/2 justify-end content-center">
+ <p className="hidden sm:block mr-3 text-center h-14 p-4 text-xs"><span className="pr-2">Share this</span> 👉</p>
+ <a className="inline-block text-white no-underline hover:text-white hover:text-underline text-center h-10 w-10 p-2 md:h-auto md:w-16 md:p-4" href="https://twitter.com/intent/tweet?url=#" style={{backgroundColor: '#33b1ff'}}>
+ <svg className="fill-current text-white h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"></path></svg>
+ </a>
+ <a className="inline-block text-white no-underline hover:text-white hover:text-underline text-center h-10 w-10 p-2 md:h-auto md:w-16 md:p-4" href="https://www.facebook.com/sharer/sharer.php?u=#" style={{backgroundColor: '#005e99'}}>
+ <svg className="fill-current text-white h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M19 6h5V0h-5c-3.86 0-7 3.14-7 7v3H8v6h4v16h6V16h5l1-6h-6V7c0-.542.458-1 1-1z"></path></svg>
+ </a>
+ </div>
+ </div>
+
+ </div>
+ {/*Progress bar*/}
+ <div id="progress" className="h-1 bg-white shadow" style={{background: "linear-gradient(to right, #4dc0b5 var(--scroll), transparent 0)"}}></div>
+</div>
+
+{/*Header*/}
+<div className="w-full m-0 p-0 bg-cover bg-bottom" style={{backgroundImage: "url('https://upload.wikimedia.org/wikipedia/commons/6/65/Toronto_Skyline_Summer_2020.jpg')", height: '60vh', maxHeight: '460px'}}>
+ <div className="container max-w-4xl bg-black bg-opacity-50 pb-16 rounded-b-xl mx-auto pt-16 md:pt-32 text-center break-normal">
+ {/*Title*/}
+ <p className="text-white font-extrabold text-3xl md:text-5xl">
+ ☕ Spring!
+ </p>
+ <p className="text-xl md:text-2xl text-gray-200">Welcome to my Blog</p>
+ </div>
+</div>
+
+<Outlet/>
+
+<footer className="bg-gray-900">
+ <div className="container max-w-6xl mx-auto flex items-center px-2 py-8">
+
+ <div className="w-full mx-auto flex flex-wrap items-center">
+ <div className="flex w-full md:w-1/2 justify-center md:justify-start text-white font-extrabold">
+ <a className="text-gray-900 no-underline hover:text-gray-900 hover:no-underline" href="#">
+ <span className="text-base text-gray-200"></span>
+ </a>
+ </div>
+ <div className="flex w-full pt-2 content-center justify-between md:w-1/2 md:justify-end">
+ <ul className="list-reset flex justify-center flex-1 md:flex-none items-center">
+ <li>
+ <a className="inline-block py-2 px-3 text-white no-underline" href="#"></a>
+ </li>
+ <li>
+ <a className="inline-block text-gray-600 no-underline hover:text-gray-200 hover:underline py-2 px-3" href="#"></a>
+ </li>
+ <li>
+ <a className="inline-block text-gray-600 no-underline hover:text-gray-200 hover:underline py-2 px-3" href="#"></a>
+ </li>
+ <li>
+ <a className="inline-block text-gray-600 no-underline hover:text-gray-200 hover:underline py-2 px-3" href="#"></a>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+
+
+ </div>
+</footer>
+
+<script src="https://unpkg.com/@popperjs/core@2"></script>
+<script src="https://unpkg.com/tippy.js@6"></script>
+<script>
+ //Init tooltips
+ tippy('.avatar')
+</script>
+</div>
</>
);
}
diff --git a/frontend/src/pages/Article.tsx b/frontend/src/pages/Article.tsx
new file mode 100644
index 0000000..b367811
--- /dev/null
+++ b/frontend/src/pages/Article.tsx
@@ -0,0 +1,25 @@
+import { useState, useEffect } from "react";
+import { useParams } from "react-router-dom";
+
+export default function Article () {
+ const { id } = useParams();
+ const [articleData, setArticleData] = useState<any>();
+
+ useEffect(() => {
+ const url = `${import.meta.env.VITE_API_TITLE}/api/v1/article/${id}`;
+ fetch(url).then((response) => {
+ if (response.ok) {
+ return response.json();
+ }
+ throw new Error("Network response was not ok.");
+ }).then((response) => setArticleData(response)); //.catch(() => navigate("/"));
+ }, [id]);
+
+ return(
+ <>
+ <h1>{articleData?.title}</h1>
+ <div>{articleData?.content}</div>
+ </>
+ );
+
+}
diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx
index cb68bde..eed745b 100644
--- a/frontend/src/pages/Home.tsx
+++ b/frontend/src/pages/Home.tsx
@@ -1,10 +1,10 @@
import { useState, useEffect } from "react";
export default function Home() {
- const [articles, setArticles] = useState<any[]>([]);
+ const [articles, setArticles] = useState<any>([]);
useEffect(() => {
- const url = `${import.meta.env.VITE_API_TITLE}/articles`;
+ const url = `${import.meta.env.VITE_API_TITLE}/api/v1/articles`;
fetch(url).then((response) => {
if (response.ok) {
return response.json();
@@ -14,19 +14,110 @@ export default function Home() {
}, []);
const allArticles = articles.map((article) => (
- <div className="border border-red-500 m-1">
- <div>{article.title}</div>
- <div>{article.content}</div>
- </div>
+ <div className="w-full md:w-1/2 p-6 flex flex-col flex-grow flex-shrink">
+ <div className="flex-1 bg-white rounded-t rounded-b-none overflow-hidden shadow-lg">
+ {/*th:href="@{/articles/{articleId}(articleId=${article.id})}"*/}
+ <a className="flex flex-wrap no-underline hover:no-underline">
+ {/*th:src="${article.photoUrl}"*/}
+ <img src={article.photoUrl} className="h-full w-full rounded-t pb-6"/>
+ {/*th:text="${article.title}"*/}
+ <div className="w-full font-bold text-xl text-gray-900 px-6">{article.title}</div>
+ <p className="text-gray-800 font-serif text-base px-6 mb-5"></p>
+ </a>
+ {/*th:if="${user.id} == ${article.createdBy.id}"*/}
+ <div ></div>
+ {/*th:href="@{/articles/edit/{articleId}(articleId=${article.id})}"*/}
+ <a className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 ml-4 text-sm rounded">Edit</a>
+ {/*th:href="@{/articles/delete/{articleId}(articleId=${article.id})}"*/}
+ <a className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 ml-4 text-sm rounded">Delete</a>
+
+ </div>
+ <div className="flex-none mt-auto bg-white rounded-b rounded-t-none overflow-hidden shadow-lg p-6">
+ <div className="flex items-center justify-between">
+ <p className="text-gray-600 text-xs md:text-sm"></p>
+ </div>
+ </div>
+ </div>
));
return(
<>
- <div className="flex justify-center p-4 flex-col justify-center">
- <h1 className="font-title text-6xl">Welcome to Spring Blog</h1>
- <div className="flex flex-col border border-red-500 mt-4">
+ {/*Container*/}
+<div className="container px-4 md:px-0 max-w-6xl mx-auto -mt-32">
+
+ <div className="mx-0 sm:mx-6">
+
+ {/*<!--Nav-->*/}
+ <nav className="mt-0 w-full">
+ <div className="container mx-auto flex items-center">
+
+ <div className="flex w-1/2 pl-4 text-sm">
+ <ul className="list-reset flex justify-between flex-1 md:flex-none items-center">
+ <li className="mr-2">
+ <a className="inline-block py-2 px-2 text-white no-underline hover:underline" href="post.html">POST</a>
+ </li>
+ <li className="mr-2">
+ <a className="inline-block text-gray-600 no-underline hover:text-gray-200 hover:underline py-2 px-2" href="multimenu post.html">MULTIMENU POST</a>
+ </li>
+ <li className="mr-2">
+ <a className="inline-block text-gray-600 no-underline hover:text-gray-200 hover:underline py-2 px-2" href="#">LINK</a>
+ </li>
+ <li className="mr-2">
+ <a className="inline-block text-gray-600 no-underline hover:text-gray-200 hover:underline py-2 px-2" href="post_vue.html">POST_VUE</a>
+ </li>
+ </ul>
+ </div>
+
+
+ <div className="flex w-1/2 justify-end content-center">
+ <a className="inline-block text-gray-500 no-underline hover:text-white hover:text-underline text-center h-10 p-2 md:h-auto md:p-4 avatar" data-tippy-content="@twitter_handle" href="https://twitter.com/intent/tweet?url=#">
+ <svg className="fill-current h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"></path></svg>
+ </a>
+ <a className="inline-block text-gray-500 no-underline hover:text-white hover:text-underline text-center h-10 p-2 md:h-auto md:p-4 avatar" data-tippy-content="#facebook_id" href="https://www.facebook.com/sharer/sharer.php?u=#">
+ <svg className="fill-current h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M19 6h5V0h-5c-3.86 0-7 3.14-7 7v3H8v6h4v16h6V16h5l1-6h-6V7c0-.542.458-1 1-1z"></path></svg>
+ </a>
+ </div>
+
+ </div>
+ </nav>
+
+ <div className="bg-gray-200 w-full text-xl md:text-2xl text-gray-800 leading-normal rounded-t">
+
+ {/*<!--Lead Card-->*/}
+ <div className="flex h-full bg-contain rounded overflow-hidden shadow-lg" style={{backgroundImage: "url('/public/abstract-polygonal-banner-background-vector.jpg')"}}>
+ <a href="post.html" className="flex flex-wrap no-underline hover:no-underline">
+ <div className="w-full md:w-2/3 rounded-t">
+ </div>
+
+ <div className="w-full md:w-1/3 flex flex-col flex-grow flex-shrink">
+ <div className="flex-1 bg-white rounded-t rounded-b-none overflow-hidden shadow-lg">
+ <p className="w-full text-gray-600 text-xs md:text-sm pt-6 px-6">Spring Blog</p>
+ <div className="w-full font-bold text-xl text-gray-900 px-6">👋 Welcome to my Java Spring Blog!</div>
+ <p className="text-gray-800 font-serif text-base px-6 mb-5">
+ This is a blog project I have created with the goal of learning and understand the ins and outs of the Java Spring framework.
+ </p>
+ </div>
+
+ <div className="flex-none mt-auto bg-white rounded-b rounded-t-none overflow-hidden shadow-lg p-6">
+ <div className="flex items-center justify-between">
+ <p className="text-gray-600 text-xs md:text-sm"></p>
+ </div>
+ </div>
+ </div>
+
+ </a>
+ </div>
+ {/*<!--/Lead Card-->*/}
+{/*th:if="${param.success}"*/}
+ <div className="text-xl p-4 bg-black text-red-500">Successful Registration!</div>
+
+
+ {/*<!--Posts Container-->*/}
+ <div className="flex flex-wrap justify-between pt-12 -mx-6">
{ allArticles }
- </div>
</div>
+ </div>
+ </div>
+</div>
</>
);
}
diff --git a/frontend/src/pages/Register.tsx b/frontend/src/pages/Register.tsx
new file mode 100644
index 0000000..7030842
--- /dev/null
+++ b/frontend/src/pages/Register.tsx
@@ -0,0 +1,55 @@
+
+export default function Register () {
+ return(
+ <>
+<div className="flex flex-col items-center justify-center bg-white p-12">
+ <div className="text-xl w-full text-center mb-8 p-4 bg-black text-red-500">Username or Email already exists</div>
+ <form role="form" method="post" className="w-full max-w-lg">
+ <div className="flex flex-wrap -mx-3 mb-6">
+ <div className="w-full md:w-1/2 px-3 mb-6 md:mb-0">
+ <label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
+ >
+ Title
+ </label>
+ <input className="appearance-none block w-full bg-gray-200 text-gray-700 border rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white"
+ id="username"
+ type="text"
+ name="username"
+ placeholder="Ted"/>
+ <p className="text-red-500 text-xs italic">Please fill out this field.</p>
+ </div>
+ <div className="w-full md:w-1/2 px-3">
+ <label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2">
+ Email
+ </label>
+ <input className="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
+ id="email"
+ type="text"
+ name="email"
+ placeholder="[email protected]"/>
+ <p className="text-red-500 text-xs italic">Please fill out this field.</p>
+ </div>
+ </div>
+ <div className="flex flex-wrap -mx-3 mb-6">
+ <div className="w-full px-3">
+ <label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2">
+ Password
+ </label>
+ <input className="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
+ id="password"
+ type="password"
+ name="password"
+ placeholder="Doe"/>
+ <p className="text-red-500 text-xs italic">Please fill out this field.</p>
+ </div>
+ </div>
+ <div className="flex flex-wrap mb-2">
+ </div>
+ <button type="submit" className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Register</button>
+
+ </form>
+</div>
+ </>
+ );
+
+}
diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx
index 450a1fe..718268a 100644
--- a/frontend/src/routes/index.tsx
+++ b/frontend/src/routes/index.tsx
@@ -1,35 +1,18 @@
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "../pages/Home";
import Layout from "../components/Layout";
+import Article from "../pages/Article";
+import Register from "../pages/Register";
export default function Index()
{
- /*
- useEffect(() => {
- const update_login_status = () => {
- //localStorage.removeItem("logged in trigger");
- const url = `${import.meta.env.VITE_API_TITLE}/api/v1/auth/data`;
- try { fetch(url, {
- credentials: "include"
- }).then((response) => {
- if(response.ok) {
- return response.json();
- }
- //throw new Error("Network response was not ok.");
- }).then((response) => response && setUserData(response.user_data)).catch((err) => { console.log(err); });}
- catch(err) { console.log(err); }
- };
- window.addEventListener('storage', update_login_status );
- update_login_status();
- return () => { window.removeEventListener('storage', update_login_status); };
- }, []);
- */
-
return (<>
<Router>
<Routes>
<Route path="/" element = {<Layout/>}>
<Route index element={<Home />} />
+ <Route path="/article/:id" element={<Article />} />
+ <Route path="register" element={<Register />} />
</Route>
</Routes>
</Router>