Implement fetching blog cards and blog entries
This commit is contained in:
parent
d4a963b162
commit
961a4cda5d
|
|
@ -12,6 +12,7 @@
|
||||||
"@babel/core": "^7.24.4",
|
"@babel/core": "^7.24.4",
|
||||||
"@babel/preset-env": "^7.24.4",
|
"@babel/preset-env": "^7.24.4",
|
||||||
"@babel/preset-react": "^7.24.1",
|
"@babel/preset-react": "^7.24.1",
|
||||||
|
"dompurify": "^3.1.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
},
|
},
|
||||||
|
|
@ -3336,6 +3337,11 @@
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
"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": {
|
"node_modules/domutils": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
"@babel/core": "^7.24.4",
|
"@babel/core": "^7.24.4",
|
||||||
"@babel/preset-env": "^7.24.4",
|
"@babel/preset-env": "^7.24.4",
|
||||||
"@babel/preset-react": "^7.24.1",
|
"@babel/preset-react": "^7.24.1",
|
||||||
|
"dompurify": "^3.1.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import NavbarButton from "./components/NavbarButton.tsx";
|
||||||
import NavbarUserInfo from "./components/NavbarUserInfo.tsx";
|
import NavbarUserInfo from "./components/NavbarUserInfo.tsx";
|
||||||
|
|
||||||
import Home from "./pages/Home.tsx";
|
import Home from "./pages/Home.tsx";
|
||||||
|
import Blog from "./pages/Blog.tsx";
|
||||||
import Login from "./pages/Login.tsx";
|
import Login from "./pages/Login.tsx";
|
||||||
import Logout from "./pages/Logout.tsx";
|
import Logout from "./pages/Logout.tsx";
|
||||||
import About from "./pages/About.tsx";
|
import About from "./pages/About.tsx";
|
||||||
|
|
@ -73,6 +74,7 @@ export default function App() {
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" exact element={<Home></Home>} />
|
<Route path="/" exact element={<Home></Home>} />
|
||||||
<Route path="/home" 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="/about" exact element={<About></About>} />
|
||||||
<Route
|
<Route
|
||||||
path="/blogeditor"
|
path="/blogeditor"
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,37 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
blogID: number;
|
||||||
blogImage: string; // This needs a placeholder/default variable
|
blogImage: string; // This needs a placeholder/default variable
|
||||||
blogTitle: string;
|
blogTitle: string;
|
||||||
blogDatePosted: string;
|
blogDatePosted: string;
|
||||||
|
blogAuthor: string;
|
||||||
blogDescription: string;
|
blogDescription: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BlogEntryCard({
|
export default function BlogEntryCard({
|
||||||
|
blogID,
|
||||||
blogImage,
|
blogImage,
|
||||||
blogTitle,
|
blogTitle,
|
||||||
blogDatePosted,
|
blogDatePosted,
|
||||||
|
blogAuthor,
|
||||||
blogDescription,
|
blogDescription,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="blogEntryCard">
|
<div className="blogEntryCard">
|
||||||
<a href="#">
|
<Link to={"/blog?ID=" + blogID.toString()}>
|
||||||
<div className="blogEntry">
|
<div className="blogEntry">
|
||||||
<img src={blogImage} className="blogEntryImage" alt="..."></img>
|
<img src={blogImage} className="blogEntryImage" alt="..."></img>
|
||||||
<div className="blogEntryBody">
|
<div className="blogEntryBody">
|
||||||
<h4 className="blogEntryTitle">{blogTitle}</h4>
|
<h4 className="blogEntryTitle">{blogTitle}</h4>
|
||||||
<p className="blogEntryDatePosted">{blogDatePosted}</p>
|
<p className="blogEntryDatePosted">{blogDatePosted}</p>
|
||||||
|
<p className="blogEntryDatePosted">{blogAuthor}</p>
|
||||||
<p className="blogEntryDescription">{blogDescription}</p>
|
<p className="blogEntryDescription">{blogDescription}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ body,
|
||||||
font-family: "consolas", sans-serif;
|
font-family: "consolas", sans-serif;
|
||||||
background-color: white;
|
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;
|
--nav-bar-height: 25px;
|
||||||
--blog-card-width: 18rem;
|
--blog-card-width: 18rem;
|
||||||
--blog-card-height: 20rem;
|
--blog-card-height: 20rem;
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,50 @@
|
||||||
import * as React from "react";
|
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() {
|
export default function Home() {
|
||||||
// A mock of what the data from the getBlogEntries JSON return would look like
|
const [blogContents, setBlogContents] = React.useState(
|
||||||
const blogEntries = [
|
"Fetching blog contents...",
|
||||||
{
|
);
|
||||||
blogImage:
|
const blogQuery = new URLSearchParams(window.location.search);
|
||||||
"https://git.cspark.dev/avatars/1c230bfe7494b1a62932d94ed8558dc61189437b1b6e0cecdb0c45c3d899bea4?size=512",
|
const blogID = blogQuery.get("ID");
|
||||||
blogTitle: "Test Blog Entry 1",
|
|
||||||
blogDatePosted: "12/04/2024 4PM", // In reality this would probably be an actual correct format/standardised timestamp
|
React.useEffect(() => {
|
||||||
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
|
if (blogID) {
|
||||||
},
|
fetch(
|
||||||
{
|
"http://localhost:8000/api/blog/contents/HTML/" + blogID.toString(),
|
||||||
blogImage:
|
{
|
||||||
"https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
|
method: "GET",
|
||||||
blogTitle: "Test Blog Entry 2",
|
headers: {
|
||||||
blogDatePosted: "8/04/2024 6PM", // In reality this would probably be an actual correct format/standardised timestamp
|
"Content-Type": "application/json",
|
||||||
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
|
},
|
||||||
},
|
},
|
||||||
];
|
)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((responseParsed: Object) => {
|
||||||
|
if (responseParsed.success) {
|
||||||
|
setBlogContents(responseParsed.contents);
|
||||||
|
} else {
|
||||||
|
setBlogContents(responseParsed.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="blogEntryGrid">
|
<div className="standardHorizontalTilingGrid">
|
||||||
{blogEntries.map((item, index) => (
|
<TilingItem>
|
||||||
<BlogEntryCard
|
<h1>Now on blog page!</h1>
|
||||||
key={index}
|
<div
|
||||||
blogImage={item["blogImage"]}
|
dangerouslySetInnerHTML={{
|
||||||
blogTitle={item["blogTitle"]}
|
__html: DOMPurify.sanitize(blogContents),
|
||||||
blogDatePosted={item["blogDatePosted"]}
|
}}
|
||||||
blogDescription={item["blogDescription"]}
|
/>
|
||||||
></BlogEntryCard>
|
</TilingItem>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,34 +3,39 @@ import * as React from "react";
|
||||||
import BlogEntryCard from "./../components/BlogEntryCard.tsx";
|
import BlogEntryCard from "./../components/BlogEntryCard.tsx";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
// A mock of what the data from the getBlogEntries JSON return would look like
|
const [blogEntries, setBlogEntries] = React.useState({});
|
||||||
const blogEntries = [
|
|
||||||
{
|
React.useEffect(() => {
|
||||||
blogImage:
|
fetch(
|
||||||
"https://git.cspark.dev/avatars/1c230bfe7494b1a62932d94ed8558dc61189437b1b6e0cecdb0c45c3d899bea4?size=512",
|
"http://127.0.0.1:8000/api/user/blog/cardInfo/?rangeStart=1&rangeEnd=25&sortByLatest=true",
|
||||||
blogTitle: "Test Blog Entry 1",
|
{
|
||||||
blogDatePosted: "12/04/2024 4PM", // In reality this would probably be an actual correct format/standardised timestamp
|
method: "GET",
|
||||||
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
|
headers: {
|
||||||
},
|
"Content-Type": "application/json",
|
||||||
{
|
},
|
||||||
blogImage:
|
},
|
||||||
"https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
|
)
|
||||||
blogTitle: "Test Blog Entry 2",
|
.then((response) => response.json())
|
||||||
blogDatePosted: "8/04/2024 6PM", // In reality this would probably be an actual correct format/standardised timestamp
|
.then((responseParsed: Object) => {
|
||||||
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
|
if (responseParsed["0"].success) {
|
||||||
},
|
delete responseParsed["0"];
|
||||||
];
|
setBlogEntries(responseParsed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="blogEntryGrid">
|
<div className="blogEntryGrid">
|
||||||
{blogEntries.map((item, index) => (
|
{Object.entries(blogEntries).map((item, index) => (
|
||||||
<BlogEntryCard
|
<BlogEntryCard
|
||||||
key={index}
|
key={index}
|
||||||
blogImage={item["blogImage"]}
|
blogID={item["1"]["blogID"]}
|
||||||
blogTitle={item["blogTitle"]}
|
blogImage={item["1"]["image"]}
|
||||||
blogDatePosted={item["blogDatePosted"]}
|
blogTitle={item["1"]["title"]}
|
||||||
blogDescription={item["blogDescription"]}
|
blogDatePosted={item["1"]["datePosted"]}
|
||||||
|
blogAuthor={item["1"]["authorInfo"]["username"]}
|
||||||
|
blogDescription={item["1"]["description"]}
|
||||||
></BlogEntryCard>
|
></BlogEntryCard>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export default function Login({ authTokenStorageHandler }: Props) {
|
||||||
rememberMe: formDataObject.rememberMe ? true : false,
|
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",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue