diff --git a/backend/src/routes/posts.ts b/backend/src/routes/posts.ts
index dc64c20..dd3ae81 100644
--- a/backend/src/routes/posts.ts
+++ b/backend/src/routes/posts.ts
@@ -49,15 +49,20 @@ postRouter.post("/", async (c) => {
}
const userId = c.get("userId");
- const blog = await prisma.post.create({
- data: {
- title: body.title,
- content: body.content,
- author_id: userId,
- },
- });
+ try {
+ const blog = await prisma.post.create({
+ data: {
+ title: body.title,
+ content: body.content,
+ author_id: userId,
+ },
+ });
- return c.json({ id: blog.id });
+ return c.json({ id: blog.id, msg: "done" });
+ } catch (e) {
+ c.status(411);
+ return c.json({ msg: "error occured while publishing the blog", error: e });
+ }
});
postRouter.get("/bulk", async (c) => {
@@ -86,6 +91,16 @@ postRouter.get("/:id", async (c) => {
where: {
id: id,
},
+ select: {
+ content: true,
+ title: true,
+ id: true,
+ author: {
+ select: {
+ name: true,
+ },
+ },
+ },
});
return c.json({
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 5d63f23..f1c9c4f 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"@pushkar1713/week13-common": "^1.0.1",
"axios": "^1.7.7",
+ "jodit-react": "^4.1.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.1"
@@ -1632,6 +1633,15 @@
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
+ "node_modules/autobind-decorator": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/autobind-decorator/-/autobind-decorator-2.4.0.tgz",
+ "integrity": "sha512-OGYhWUO72V6DafbF8PM8rm3EPbfuyMZcJhtm5/n26IDwO18pohE4eNazLoCGhPiXOCD0gEGmrbU3849QvM8bbw==",
+ "engines": {
+ "node": ">=8.10",
+ "npm": ">=6.4.1"
+ }
+ },
"node_modules/autoprefixer": {
"version": "10.4.20",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
@@ -2725,6 +2735,26 @@
"jiti": "bin/jiti.js"
}
},
+ "node_modules/jodit": {
+ "version": "4.2.27",
+ "resolved": "https://registry.npmjs.org/jodit/-/jodit-4.2.27.tgz",
+ "integrity": "sha512-cqqeunB3HMElnocVhs5Qq2bhgpMIT2vKQPBpKcOTWKvX6GJ0GYAIneMEf43lphJuo+119CvBE8YgljD5iTfsAQ==",
+ "dependencies": {
+ "autobind-decorator": "^2.4.0"
+ }
+ },
+ "node_modules/jodit-react": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/jodit-react/-/jodit-react-4.1.2.tgz",
+ "integrity": "sha512-Hs1evpM1IK5zvy/5m5Gk819L8aC+9EmEdQvCoLHVUr/R3vtH4nYFD6wsMRj3ur3J4ZHhaSBjt0N3R7ggwP405Q==",
+ "dependencies": {
+ "jodit": "^4.2.10"
+ },
+ "peerDependencies": {
+ "react": "~0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "~0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index cd9dcc9..cbf8c4f 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -12,6 +12,7 @@
"dependencies": {
"@pushkar1713/week13-common": "^1.0.1",
"axios": "^1.7.7",
+ "jodit-react": "^4.1.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.1"
diff --git a/frontend/src/assets/logo.png b/frontend/src/assets/logo.png
new file mode 100644
index 0000000..2297ee7
Binary files /dev/null and b/frontend/src/assets/logo.png differ
diff --git a/frontend/src/components/appbar.tsx b/frontend/src/components/appbar.tsx
new file mode 100644
index 0000000..0379823
--- /dev/null
+++ b/frontend/src/components/appbar.tsx
@@ -0,0 +1,28 @@
+import { Avatar } from "./blogCard";
+import { Link } from "react-router-dom";
+import Logo from "../assets/logo.png";
+
+export const Appbar = () => {
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/components/blogCard.tsx b/frontend/src/components/blogCard.tsx
index 7fb7a44..b2f85b2 100644
--- a/frontend/src/components/blogCard.tsx
+++ b/frontend/src/components/blogCard.tsx
@@ -1,39 +1,72 @@
+import { Link } from "react-router-dom";
+
interface blogCardProps {
authorName: String;
title: String;
content: String;
date: String;
+ id: String;
}
export const BlogCard = ({
+ id,
authorName,
title,
content,
date,
}: blogCardProps) => {
return (
-
-
-
-
-
+
+
+
+
+
+
{authorName}
+ {date}
+
+
+ {title}
+
+
{`Reading time : ${Math.ceil(
+ content.length / 200
+ )} minutes`}
+
+ {content.length > 100 ? (
+
+ ) : (
+
+ )}
-
{authorName}
- {date}
-
{title}
-
{content.length > 100 ? content.slice(0, 200) : content}...
-
{`Reading time : ${Math.ceil(content.length / 200)} minutes`}
-
-
+
);
};
-const Avatar = ({ name }: { name: String }) => {
+export function Avatar({
+ name,
+ size = "small",
+}: {
+ name: String;
+ size?: "small" | "big";
+}) {
return (
-
-
{name[0]}
+
+
+ {name[0]}
+
);
-};
+}
diff --git a/frontend/src/components/footer.tsx b/frontend/src/components/footer.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/components/fullBlog.tsx b/frontend/src/components/fullBlog.tsx
new file mode 100644
index 0000000..e98c154
--- /dev/null
+++ b/frontend/src/components/fullBlog.tsx
@@ -0,0 +1,42 @@
+import { Blogs } from "../hooks/hooks";
+import { Avatar } from "./blogCard";
+
+export const FullBlog = ({ blog }: { blog: Blogs }) => {
+ return (
+
+
+
+
+
+ {blog.title}
+
+
+ Published on 2nd December 2023
+
+
+
+
+
Author
+
+
+
+
+ {blog.author.name || "Anonymous"}
+
+
+ Your first blog posts won’t be perfect, but you just have to
+ do it. You have to start somewhere — Shane Barker
+
+
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/components/header.tsx b/frontend/src/components/header.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/hooks/useBlog.tsx b/frontend/src/hooks/hooks.tsx
similarity index 57%
rename from frontend/src/hooks/useBlog.tsx
rename to frontend/src/hooks/hooks.tsx
index fc6cca8..bd88f40 100644
--- a/frontend/src/hooks/useBlog.tsx
+++ b/frontend/src/hooks/hooks.tsx
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import axios from "axios";
import { BACKEND_URL } from "../config";
-interface Blogs {
+export interface Blogs {
content: string;
title: string;
id: string;
@@ -11,6 +11,28 @@ interface Blogs {
};
}
+export const userBlogs = ({ id }: { id: string }) => {
+ const [loading, setLoading] = useState(true);
+ const [blog, setBlog] = useState
();
+
+ useEffect(() => {
+ axios
+ .get(`${BACKEND_URL}/api/v1/blog/${id}`, {
+ headers: {
+ Authorization: "Bearer " + localStorage.getItem("token"),
+ },
+ })
+ .then((response) => {
+ setBlog(response.data.blog);
+ setLoading(false);
+ });
+ }, [id]);
+ return {
+ loading,
+ blog,
+ };
+};
+
export const useBlog = () => {
const [loading, setLoading] = useState(true);
const [blogs, setBlogs] = useState([]);
diff --git a/frontend/src/pages/blog.tsx b/frontend/src/pages/blog.tsx
index 1861607..d48b60d 100644
--- a/frontend/src/pages/blog.tsx
+++ b/frontend/src/pages/blog.tsx
@@ -1,8 +1,30 @@
+import { Appbar } from "../components/appbar";
+import { FullBlog } from "../components/fullBlog";
+import { userBlogs } from "../hooks/hooks";
+import { useParams } from "react-router-dom";
+
+// atomFamilies/selectorFamilies
const Blog = () => {
+ const { id } = useParams();
+ const { loading, blog } = userBlogs({
+ id: id || "",
+ });
+
+ if (loading || !blog) {
+ return (
+
+ );
+ }
return (
- <>
- blog
- >
+
);
};
diff --git a/frontend/src/pages/blogs.tsx b/frontend/src/pages/blogs.tsx
index 9705c0f..8fd86e8 100644
--- a/frontend/src/pages/blogs.tsx
+++ b/frontend/src/pages/blogs.tsx
@@ -1,5 +1,6 @@
+import { Appbar } from "../components/appbar";
import { BlogCard } from "../components/blogCard";
-import { useBlog } from "../hooks/useBlog";
+import { useBlog } from "../hooks/hooks";
export const Blogs = () => {
const { loading, blogs } = useBlog();
@@ -10,12 +11,17 @@ export const Blogs = () => {
return (
+
+
+ Pens and Pixels Blog
+
{blogs.map((blog) => (
))}
diff --git a/frontend/src/pages/home.tsx b/frontend/src/pages/home.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/pages/publish.tsx b/frontend/src/pages/publish.tsx
new file mode 100644
index 0000000..813d3ee
--- /dev/null
+++ b/frontend/src/pages/publish.tsx
@@ -0,0 +1,93 @@
+import axios from "axios";
+import { BACKEND_URL } from "../config";
+import { useNavigate } from "react-router-dom";
+import { useState, useRef } from "react";
+import JoditEditor from "jodit-react";
+import { Appbar } from "../components/appbar";
+
+export const Publish = () => {
+ const [title, setTitle] = useState("");
+ const [content, setContent] = useState("");
+ const navigate = useNavigate();
+ const editor = useRef(null);
+
+ return (
+
+
+
+ Start Writing
+
+
+
+ {
+ setTitle(e.target.value);
+ }}
+ type="text"
+ className=" bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 my-6"
+ placeholder="Title"
+ />
+ {/* {
+ setContent(e.target.value);
+ }}
+ /> */}
+ {
+ setContent(newContent);
+ }}
+ />
+
+
+
+
+ );
+};
+
+// function TextEditor({
+// onChange,
+// }: {
+// onChange: (e: ChangeEvent) => void;
+// }) {
+// return (
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+// );
+// }
diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx
index d2b650f..f45cbc6 100644
--- a/frontend/src/router.tsx
+++ b/frontend/src/router.tsx
@@ -4,6 +4,7 @@ import Blog from "./pages/blog";
import SignUp from "./pages/signup";
import SignIn from "./pages/signin";
import { Blogs } from "./pages/blogs";
+import { Publish } from "./pages/publish";
export const appRouter = createBrowserRouter([
{
path: "/",
@@ -18,13 +19,17 @@ export const appRouter = createBrowserRouter([
element: ,
},
{
- path: "/blog",
+ path: "/blog/:id",
element: ,
},
{
path: "/blogs",
element: ,
},
+ {
+ path: "/publish",
+ element: ,
+ },
],
},
]);
diff --git a/tasks.md b/tasks.md
new file mode 100644
index 0000000..d082e1e
--- /dev/null
+++ b/tasks.md
@@ -0,0 +1,4 @@
+- add skeleton
+- add spinner
+- add homepage
+- add header and footer