foundation for posts
This commit is contained in:
@@ -1,10 +1,17 @@
|
|||||||
export default function PostRow() {
|
import type { Post } from "../utils/types";
|
||||||
|
|
||||||
|
export default function PostRow({ post }: { post: Post }) {
|
||||||
return (
|
return (
|
||||||
<a href="" className="hover:underline">
|
<div className="block">
|
||||||
<div>
|
<a href={`/post/${post.slug}`} className="hover:underline">
|
||||||
<h1>This is a test post</h1>
|
<div>
|
||||||
<p>This is a test post description to show how cool it is</p>
|
<h1>{post.title}</h1>
|
||||||
</div>
|
<p>{post.description}</p>
|
||||||
</a>
|
<p className="text-xs opacity-50">
|
||||||
|
Posted on {new Date(post.createdAt).toLocaleString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import "./assets/index.css";
|
|||||||
|
|
||||||
import Home from "./pages/home";
|
import Home from "./pages/home";
|
||||||
import LogIn from "./pages/login";
|
import LogIn from "./pages/login";
|
||||||
|
import Post from "./pages/post";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@@ -16,6 +17,10 @@ const router = createBrowserRouter([
|
|||||||
path: "/login",
|
path: "/login",
|
||||||
element: <LogIn />,
|
element: <LogIn />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/post/:slug",
|
||||||
|
element: <Post />,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
|
|||||||
@@ -1,7 +1,45 @@
|
|||||||
import Layout from "../components/layout";
|
import Layout from "../components/layout";
|
||||||
import PostRow from "../components/postRow";
|
import PostRow from "../components/postRow";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import type { Post } from "../utils/types";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
const [posts, setPosts] = useState<Post[] | null>(null);
|
||||||
|
const [loaded, setLoaded] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch("/api/posts", {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
setPosts(data);
|
||||||
|
setLoaded(true);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load posts!");
|
||||||
|
setLoaded(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const PostList = () => {
|
||||||
|
const mappedPosts = posts?.map((v, k) => {
|
||||||
|
return <PostRow key={k} post={v} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
if ((loaded && !posts) || posts?.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="my-2 text-center">
|
||||||
|
<h1 className="text-2xl font-bold">uh oh!</h1>
|
||||||
|
<p className="opacity-50 text-sm">there's nothing here..</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (loaded) {
|
||||||
|
return mappedPosts;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<div className="mb-4 space-y-1">
|
<div className="mb-4 space-y-1">
|
||||||
@@ -15,7 +53,7 @@ export default function Home() {
|
|||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<h1 className="font-semibold">Posts</h1>
|
<h1 className="font-semibold">Posts</h1>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<PostRow />
|
<PostList />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import Layout from "../components/layout";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import type { Post } from "../utils/types";
|
||||||
|
|
||||||
|
export default function Post() {
|
||||||
|
const [post, setPost] = useState<Post | null>(null);
|
||||||
|
const params = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (params.slug) {
|
||||||
|
fetch("/api/posts/" + params.slug, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
setPost(data);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load post! " + res.status);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(post);
|
||||||
|
}, [post]);
|
||||||
|
|
||||||
|
if (post) {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className="my-2">
|
||||||
|
<h1 className="text-2xl font-bold">{post.title}</h1>
|
||||||
|
<p className="text-sm opacity-50">{post.description}</p>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,12 +6,7 @@ import {
|
|||||||
type ReactNode,
|
type ReactNode,
|
||||||
useEffect,
|
useEffect,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
import type { User } from "../utils/types";
|
||||||
interface User {
|
|
||||||
id: number;
|
|
||||||
email: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
interface AuthContextType {
|
interface AuthContextType {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
refetch: () => void;
|
refetch: () => void;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
interface Post {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
slug: string;
|
||||||
|
content: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
userId: number;
|
||||||
|
}
|
||||||
|
interface User {
|
||||||
|
id: number;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { Post, User };
|
||||||
Reference in New Issue
Block a user