summaryrefslogtreecommitdiffhomepage
path: root/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/pages/Home.tsx53
-rw-r--r--frontend/src/pages/articles/Article.tsx23
-rw-r--r--frontend/src/pages/articles/Edit.tsx142
-rw-r--r--frontend/src/routes/index.tsx12
4 files changed, 206 insertions, 24 deletions
diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx
index 71683e7..df9b4c1 100644
--- a/frontend/src/pages/Home.tsx
+++ b/frontend/src/pages/Home.tsx
@@ -5,6 +5,7 @@ type article = {
title: string;
photoUrl: string;
content: string;
+ createdBy: string;
createdOn: string;
updateOn: string;
};
@@ -14,11 +15,12 @@ type articleSearch = {
value: string | null;
};
-
export default function Home({
articleSearch,
+ username,
}: {
articleSearch: articleSearch;
+ username: string | null;
}) {
const [articles, setArticles] = useState<article[]>([]);
const [allArticles, setAllArticles] = useState<JSX.Element[]>([]);
@@ -40,12 +42,30 @@ export default function Home({
if (!response.ok) {
console.log(response);
alert("check console for error");
+ } else {
+ fetchArticles();
}
- else {
- fetchArticles();
- }
};
+ function renderButtons(article: article) {
+ return (
+ <div>
+ <a
+ href={`/article/edit/${article.id}`}
+ className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 ml-4 text-sm rounded"
+ >
+ Edit
+ </a>
+ <form onSubmit={handleDelete} method="post">
+ <input type="hidden" name="id" value={article.id} />
+ <button className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 ml-4 text-sm rounded">
+ Delete
+ </button>
+ </form>
+ </div>
+ );
+ }
+
const fetchArticles = useCallback(() => {
let url;
if (articleSearch.value === null) {
@@ -68,7 +88,9 @@ export default function Home({
}, [articleSearch.value]);
// pull data when new search is given
- useEffect(() => { fetchArticles(); } , [articleSearch.value, fetchArticles]);
+ useEffect(() => {
+ fetchArticles();
+ }, [articleSearch.value, fetchArticles]);
// when new data is pulled update the articles shown
useEffect(() => {
@@ -80,7 +102,10 @@ export default function Home({
>
<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">
+ <a
+ href={`/article/${article.id}`}
+ className="flex flex-wrap no-underline hover:bg-sky-200 border-b-2 hover:no-underline"
+ >
<img
src={article.photoUrl}
className="h-full w-full rounded-t pb-6"
@@ -90,19 +115,7 @@ export default function Home({
</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})}"*/}
- <form onSubmit={handleDelete} method="post">
- <input type="hidden" name="id" value={article.id}/>
- <button className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 ml-4 text-sm rounded">
- Delete
- </button>
- </form>
+ {username == article?.createdBy && renderButtons(article)}
</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">
@@ -112,7 +125,7 @@ export default function Home({
</div>
)),
);
- }, [articles]);
+ }, [articles, username]);
return (
<>
diff --git a/frontend/src/pages/articles/Article.tsx b/frontend/src/pages/articles/Article.tsx
index 1a96ada..633e418 100644
--- a/frontend/src/pages/articles/Article.tsx
+++ b/frontend/src/pages/articles/Article.tsx
@@ -7,7 +7,7 @@ type article = {
photoUrl: string;
content: string;
createdOn: string;
- updateOn: string;
+ updatedOn: string;
};
export default function Article() {
@@ -28,8 +28,25 @@ export default function Article() {
return (
<>
- <h1>{articleData?.title}</h1>
- <div>{articleData?.content}</div>
+ <div className="text-center pt-16 md:pt-32">
+ <p className="text-sm md:text-base text-green-500 font-bold">
+ {articleData?.createdOn}
+ </p>
+ <h1 className="font-bold break-normal text-3xl md:text-5xl">
+ {articleData?.title}
+ </h1>
+ </div>
+ <div
+ className="container w-full max-w-6xl mx-auto bg-white bg-cover mt-8 rounded"
+ style={{ background: `url('${articleData?.photoUrl}'); height: 75vh;` }}
+ ></div>
+ <div className="container max-w-5xl mx-auto -mt-32">
+ <div className="mx-0 sm:mx-6">
+ <div className="bg-white w-full p-8 md:p-24 text-xl md:text-2xl text-gray-800 leading-normal">
+ <div>{articleData?.content}</div>
+ </div>
+ </div>
+ </div>
</>
);
}
diff --git a/frontend/src/pages/articles/Edit.tsx b/frontend/src/pages/articles/Edit.tsx
new file mode 100644
index 0000000..4d8e2d8
--- /dev/null
+++ b/frontend/src/pages/articles/Edit.tsx
@@ -0,0 +1,142 @@
+import { useState, useEffect, FormEvent, ChangeEvent } from "react";
+import { useParams, useNavigate } from "react-router-dom";
+
+type article = {
+ id: number;
+ title: string;
+ photoUrl: string;
+ content: string;
+ createdOn: string;
+ updatedOn: string;
+};
+
+export default function EditArticle() {
+ const navigate = useNavigate();
+ const { id } = useParams();
+ const [articleData, setArticleData] = useState<article>({
+ id: 0,
+ title: "",
+ photoUrl: "",
+ content: "",
+ createdOn: "",
+ updatedOn: "",
+ });
+
+ 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));
+ }, [id]);
+
+ const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
+ setArticleData({ ...articleData, [e.target.name]: e.target.value });
+ };
+
+ const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
+ e.preventDefault(); //stops submit from happening
+
+ const target = e.target as typeof e.target & {
+ title: { value: string };
+ photoUrl: { value: string };
+ content: { value: string };
+ };
+
+ const formData = new FormData();
+ formData.append("title", target.title.value);
+ formData.append("photoUrl", target.photoUrl.value);
+ formData.append("content", target.content.value);
+
+ const response = await fetch(
+ `${import.meta.env.VITE_API_TITLE}/api/v1/articles/edit/${articleData.id}`,
+ {
+ credentials: "include",
+ method: "post",
+ body: formData,
+ },
+ );
+ if (response.ok) {
+ navigate("/");
+ } else {
+ console.log(response);
+ alert("check console for error");
+ }
+ };
+
+ return (
+ <>
+ <div className="flex justify-center bg-white p-12">
+ <form onSubmit={handleSubmit} method="post" className="w-full max-w-lg">
+ <input type="hidden" />
+ <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="title"
+ type="text"
+ name="title"
+ value={articleData.title}
+ onChange={handleChange}
+ placeholder="Yep"
+ />
+ <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">
+ Photo URL
+ </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="photoUrl"
+ type="text"
+ name="photoUrl"
+ value={articleData.photoUrl}
+ onChange={handleChange}
+ placeholder="Doe"
+ />
+ <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">
+ Content
+ </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="content"
+ type="text"
+ name="content"
+ value={articleData.content}
+ onChange={handleChange}
+ 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"
+ >
+ Update
+ </button>
+ </form>
+ </div>
+ </>
+ );
+}
diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx
index cee3529..df38459 100644
--- a/frontend/src/routes/index.tsx
+++ b/frontend/src/routes/index.tsx
@@ -4,6 +4,7 @@ import Home from "../pages/Home";
import Layout from "../components/Layout";
import Article from "../pages/articles/Article";
import NewArticle from "../pages/articles/New";
+import EditArticle from "../pages/articles/Edit";
import Register from "../pages/auth/Register";
import Login from "../pages/auth/Login";
@@ -33,9 +34,18 @@ export default function Index() {
<Layout user={userProp} articleSearch={articleSearchProp} />
}
>
- <Route index element={<Home articleSearch={articleSearchProp} />} />
+ <Route
+ index
+ element={
+ <Home
+ username={userProp.value}
+ articleSearch={articleSearchProp}
+ />
+ }
+ />
<Route path="/article/:id" element={<Article />} />
<Route path="/article/new" element={<NewArticle />} />
+ <Route path="/article/edit/:id" element={<EditArticle />} />
<Route path="register" element={<Register />} />
<Route path="login" element={<Login user={userProp} />} />
</Route>