Implement fetching blog cards and blog entries

This commit is contained in:
Curt Spark 2024-05-08 20:58:18 +01:00
parent d4a963b162
commit 961a4cda5d
8 changed files with 87 additions and 54 deletions

6
package-lock.json generated
View File

@ -12,6 +12,7 @@
"@babel/core": "^7.24.4",
"@babel/preset-env": "^7.24.4",
"@babel/preset-react": "^7.24.1",
"dompurify": "^3.1.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
@ -3336,6 +3337,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.2.tgz",
"integrity": "sha512-hLGGBI1tw5N8qTELr3blKjAML/LY4ANxksbS612UiJyDfyf/2D092Pvm+S7pmeTGJRqvlJkFzBoHBQKgQlOQVg=="
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",

View File

@ -31,6 +31,7 @@
"@babel/core": "^7.24.4",
"@babel/preset-env": "^7.24.4",
"@babel/preset-react": "^7.24.1",
"dompurify": "^3.1.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}

View File

@ -6,6 +6,7 @@ import NavbarButton from "./components/NavbarButton.tsx";
import NavbarUserInfo from "./components/NavbarUserInfo.tsx";
import Home from "./pages/Home.tsx";
import Blog from "./pages/Blog.tsx";
import Login from "./pages/Login.tsx";
import Logout from "./pages/Logout.tsx";
import About from "./pages/About.tsx";
@ -73,6 +74,7 @@ export default function App() {
<Routes>
<Route path="/" exact element={<Home></Home>} />
<Route path="/home" exact element={<Home></Home>} />
<Route path="/blog/*" exact element={<Blog></Blog>} />
<Route path="/about" exact element={<About></About>} />
<Route
path="/blogeditor"

View File

@ -1,31 +1,37 @@
import * as React from "react";
import { Link } from "react-router-dom";
interface Props {
blogID: number;
blogImage: string; // This needs a placeholder/default variable
blogTitle: string;
blogDatePosted: string;
blogAuthor: string;
blogDescription: string;
}
export default function BlogEntryCard({
blogID,
blogImage,
blogTitle,
blogDatePosted,
blogAuthor,
blogDescription,
}: Props) {
return (
<>
<div className="blogEntryCard">
<a href="#">
<Link to={"/blog?ID=" + blogID.toString()}>
<div className="blogEntry">
<img src={blogImage} className="blogEntryImage" alt="..."></img>
<div className="blogEntryBody">
<h4 className="blogEntryTitle">{blogTitle}</h4>
<p className="blogEntryDatePosted">{blogDatePosted}</p>
<p className="blogEntryDatePosted">{blogAuthor}</p>
<p className="blogEntryDescription">{blogDescription}</p>
</div>
</div>
</a>
</Link>
</div>
</>
);

View File

@ -12,7 +12,8 @@ body,
font-family: "consolas", sans-serif;
background-color: white;
--background-image: url(https://source.unsplash.com/random/1920x1080/?blurry);
/*--background-image: url(https://source.unsplash.com/random/1920x1080/?blurry);*/
--background-image: url(https://images.unsplash.com/photo-1542254503-d802f00c2342?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=1080&ixid=MnwxfDB8MXxyYW5kb218MHx8Ymx1cnJ5fHx8fHx8MTcxNTE3MDY0OQ&ixlib=rb-4.0.3&q=80&w=1920);
--nav-bar-height: 25px;
--blog-card-width: 18rem;
--blog-card-height: 20rem;

View File

@ -1,38 +1,50 @@
import * as React from "react";
import * as DOMPurify from "dompurify";
import BlogEntryCard from "./../components/BlogEntryCard.tsx";
import { Navigate } from "react-router-dom";
import TilingItem from "../components/TilingItem.tsx";
export default function Home() {
// A mock of what the data from the getBlogEntries JSON return would look like
const blogEntries = [
{
blogImage:
"https://git.cspark.dev/avatars/1c230bfe7494b1a62932d94ed8558dc61189437b1b6e0cecdb0c45c3d899bea4?size=512",
blogTitle: "Test Blog Entry 1",
blogDatePosted: "12/04/2024 4PM", // In reality this would probably be an actual correct format/standardised timestamp
blogDescription: "This is a first blog entry test", // If no description, we'd want to get a small snippet of the intro of the blog
},
{
blogImage:
"https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
blogTitle: "Test Blog Entry 2",
blogDatePosted: "8/04/2024 6PM", // In reality this would probably be an actual correct format/standardised timestamp
blogDescription: "This is a test blog entry number 2", // If no description, we'd want to get a small snippet of the intro of the blog
},
];
const [blogContents, setBlogContents] = React.useState(
"Fetching blog contents...",
);
const blogQuery = new URLSearchParams(window.location.search);
const blogID = blogQuery.get("ID");
React.useEffect(() => {
if (blogID) {
fetch(
"http://localhost:8000/api/blog/contents/HTML/" + blogID.toString(),
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
},
)
.then((response) => response.json())
.then((responseParsed: Object) => {
if (responseParsed.success) {
setBlogContents(responseParsed.contents);
} else {
setBlogContents(responseParsed.message);
}
});
}
}, []);
return (
<>
<div className="blogEntryGrid">
{blogEntries.map((item, index) => (
<BlogEntryCard
key={index}
blogImage={item["blogImage"]}
blogTitle={item["blogTitle"]}
blogDatePosted={item["blogDatePosted"]}
blogDescription={item["blogDescription"]}
></BlogEntryCard>
))}
<div className="standardHorizontalTilingGrid">
<TilingItem>
<h1>Now on blog page!</h1>
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(blogContents),
}}
/>
</TilingItem>
</div>
</>
);

View File

@ -3,34 +3,39 @@ import * as React from "react";
import BlogEntryCard from "./../components/BlogEntryCard.tsx";
export default function Home() {
// A mock of what the data from the getBlogEntries JSON return would look like
const blogEntries = [
{
blogImage:
"https://git.cspark.dev/avatars/1c230bfe7494b1a62932d94ed8558dc61189437b1b6e0cecdb0c45c3d899bea4?size=512",
blogTitle: "Test Blog Entry 1",
blogDatePosted: "12/04/2024 4PM", // In reality this would probably be an actual correct format/standardised timestamp
blogDescription: "This is a first blog entry test", // If no description, we'd want to get a small snippet of the intro of the blog
},
{
blogImage:
"https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
blogTitle: "Test Blog Entry 2",
blogDatePosted: "8/04/2024 6PM", // In reality this would probably be an actual correct format/standardised timestamp
blogDescription: "This is a test blog entry number 2", // If no description, we'd want to get a small snippet of the intro of the blog
},
];
const [blogEntries, setBlogEntries] = React.useState({});
React.useEffect(() => {
fetch(
"http://127.0.0.1:8000/api/user/blog/cardInfo/?rangeStart=1&rangeEnd=25&sortByLatest=true",
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
},
)
.then((response) => response.json())
.then((responseParsed: Object) => {
if (responseParsed["0"].success) {
delete responseParsed["0"];
setBlogEntries(responseParsed);
}
});
}, []);
return (
<>
<div className="blogEntryGrid">
{blogEntries.map((item, index) => (
{Object.entries(blogEntries).map((item, index) => (
<BlogEntryCard
key={index}
blogImage={item["blogImage"]}
blogTitle={item["blogTitle"]}
blogDatePosted={item["blogDatePosted"]}
blogDescription={item["blogDescription"]}
blogID={item["1"]["blogID"]}
blogImage={item["1"]["image"]}
blogTitle={item["1"]["title"]}
blogDatePosted={item["1"]["datePosted"]}
blogAuthor={item["1"]["authorInfo"]["username"]}
blogDescription={item["1"]["description"]}
></BlogEntryCard>
))}
</div>

View File

@ -31,7 +31,7 @@ export default function Login({ authTokenStorageHandler }: Props) {
rememberMe: formDataObject.rememberMe ? true : false,
};
fetch("http://127.0.0.1:8000/api/login", {
fetch("http://127.0.0.1:8000/api/user/login", {
method: "POST",
headers: {
"Content-Type": "application/json",