Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>TechTalk</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
3 changes: 3 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HomePage, PostForm, NotFoundPage } from "./pages/index";
import { Routes, Route } from "react-router-dom";
import { PostProvider } from "./context/postContext";
import { Toaster } from "react-hot-toast";

function App() {
return (
Expand All @@ -10,8 +11,10 @@ function App() {
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/new" element={<PostForm />} />
<Route path="/posts/:id" element={<PostForm />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
<Toaster/>
</PostProvider>

</div>
Expand Down
10 changes: 8 additions & 2 deletions src/api/posts.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import axios from 'axios';

export const getPostsRequests = async () => await axios.get('http://localhost:4000/api/posts')
export const getPostsRequest = async () => await axios.get('http://localhost:4000/api/posts')

export const createPostsRequests = async (post) => await axios.post('http://localhost:4000/api/posts', post)
export const createPostRequest = async (post) => await axios.post('http://localhost:4000/api/posts', post)

export const deletePostRequest = async id => await axios.delete("http://localhost:4000/api/posts/" + id)

export const getPostRequest = async id => await axios.get("http://localhost:4000/api/posts/" + id)

export const updatePostRequest = async (id, newFields) => await axios.put(`http://localhost:4000/api/posts/${id}`, newFields)
70 changes: 70 additions & 0 deletions src/components/PostCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import toast from "react-hot-toast";
import { usePosts } from "../context/postContext";
import { useNavigate } from "react-router-dom";

export function PostCard({ post }) {
const { deletePost } = usePosts();
const navigate = useNavigate();

const handleDelete = (id) => {
toast(
(t) => (
<div>
<p className="text-white">
{" "}
Do you want to Delete? <strong>{id}</strong>
</p>
<div>
<button
className="bg-red-500 hover:bg-red-400 px-3 py-2 text-sm text-white rounded-sm mx-2"
onClick={() => {
deletePost(id);
toast.dismiss(t.id);
}}
>
Delete
</button>

<button
className="bg-slate-400 hover:bg-slate-500 px-3 py-2 text-white rounded-sm mx-2"
onClick={() => toast.dismiss(t.id)}
>
Cancel
</button>
</div>
</div>
),
{
style: {
background: "#202020",
},
}
);
};

return (
<div
className="bg-zinc-800 text-white rounded-sm shadow-md shadow-black hover:bg-zinc-700
hover:cursor-pointer"
>
<div className="px-4 py-7">
<div className="flex justify-between">
<h3>{post.title}</h3>
<button
className="bg-sky-400 text-sm px-2 py-1 rounded-sm"
onClick={() => navigate(`/posts/${post._id}`)}
>
Edit
</button>
<button
className="bg-red-600 text-sm px-2 py-1 rounded-sm"
onClick={() => handleDelete(post._id)}
>
Delete
</button>
</div>
<p>{post.description}</p>
</div>
</div>
);
}
49 changes: 35 additions & 14 deletions src/context/postContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { useState, createContext, useContext, useEffect } from "react";
import { getPostsRequests, createPostsRequests } from "../api/posts";
import {
getPostsRequest,
createPostRequest,
deletePostRequest,
getPostRequest,
updatePostRequest,
} from "../api/posts";
export const postContext = createContext();

export const usePosts = () => {
Expand All @@ -10,30 +16,45 @@ export const usePosts = () => {
export const PostProvider = ({ children }) => {
const [posts, setPosts] = useState([]);

useEffect(() => {
(async () => {
const res = await getPostsRequest();
setPosts(res.data);
})();
}, []);

const createPost = async (post) => {
const res = await createPostRequest(post);
setPosts([...posts, res.data]);
};


const getPosts = async () => {
const res = await getPostsRequests();
setPosts(res.data)
const deletePost = async (id) => {
await deletePostRequest(id);
setPosts(posts.filter((post) => post._id !== id));
};

const createPost = async (post) =>{
const res = await createPostsRequests(post)
setPosts([...posts, res.data])
const getPost = async (id) => {
try {
const res = await getPostRequest(id);
return res.data;
} catch (error) {
console.error(error);
}
};

useEffect(()=>{
getPosts()
}, [])

const updatePost = async (id, post) => {
const res = await updatePostRequest(id, post);
setPosts(post.map((post) => (post.id === id ? res.data : post)));
};

return (
<postContext.Provider
value={{
posts,
getPosts,
createPost
createPost,
deletePost,
getPost,
updatePost,
}}
>
{children}
Expand Down
11 changes: 6 additions & 5 deletions src/pages/HomePage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { usePosts } from "../context/postContext";
import {VscEmptyWindow} from 'react-icons/vsc'
import { Link } from "react-router-dom";

import { PostCard } from "../components/PostCard";
export function HomePage() {

const { posts } = usePosts()
Expand All @@ -17,11 +17,12 @@ export function HomePage() {
<div className='text-white'>

<Link to="/new">Create new Post</Link>
{posts.map(post => (
<div key={post._id}>
{post.title}
</div>

<div className="grid gap-4">
{posts.map(post => (
<PostCard post={post} key={post._id}/>
))}
</div>
</div>
);
}
91 changes: 75 additions & 16 deletions src/pages/PostForm.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,113 @@
import { Formik, Form, Field, ErrorMessage } from "formik";
import { usePosts } from "../context/postContext";
import { useNavigate } from "react-router-dom";
import { useNavigate, useParams, Link} from "react-router-dom";
import * as Yup from "yup";
import { useEffect, useState } from "react";

export function PostForm() {
const { createPost } = usePosts();
const { createPost, getPost, updatePost } = usePosts();
const navigate = useNavigate();
const [post, setPost] = useState({
title: "",
description: "",
image: null,
});
const params = useParams();

useEffect(() => {
(async () => {
if (params.id) {
const post = await getPost(params.id);
setPost({
title: post.title,
description: post.description,
});
}
})();
}, [params.id, getPost]);

return (
<div>
<div className="flex items-center justify-center">
<div className="bg-zinc-800 p-10 shadow-md shadow-black">
<header className="flex justify-between items-center py-4 text-white">
<h3 className="text-xl">New Post</h3>
<Link to="/" className="text-gray-400 text-sm hover:text-gray-300">
Go Back
</Link>
</header>

<Formik
initialValues={{
title: "",
description: "",
}}
initialValues={post}
validationSchema={Yup.object({
title: Yup.string().required("Title is Required"),
description: Yup.string().required("Description is Required"),
})}
onSubmit={async (values, actions) => {
await createPost(values);

if(params.id){
await updatePost(params.id, values)
}else{
await createPost(values);
}
navigate("/");
}}
enableReinitialize
>
{({ handleSubmit }) => (
<Form onSubmit={handleSubmit}>


<label
htmlFor="title"
className="text-sm block font-bold text-gray-400"
>
Title
</label>
<Field
name="title"
placeholder="Title"
className='px-3 py-2 focus:outline-none rounded bg-gray-600 text-white w-full'
className="px-3 py-2 focus:outline-none rounded bg-gray-600 text-white w-full"
/>
<ErrorMessage
compone="p"
component="p"
name="title"
className="text-red-400 text-sm"
/>

<label
htmlFor="description"
className="text-sm block font-bold text-gray-400"
>
Description
</label>
<Field
name='description'
component="textarea"
name="description"
placeholder="Description"
className='px-3 py-2 focus:outline-none rounded bg-gray-600 text-white w-full'
className="px-3 py-2 focus:outline-none rounded bg-gray-600 text-white w-full"
rows={5}
/>
<ErrorMessage
component="p"
name='description'
className='text-red-400 text-sm'
name="description"
className="text-red-400 text-sm"
/>
<button type="submit">Save</button>
<label
htmlFor="title"
className="text-sm block font-bold text-gray-400"
>
Imagen
</label>
<input type="file" name="image" className=""/>
<button
type="submit"
className="bg-indigo-600 hover:bg-indigo-500 px-4 py-2 rounded mt-2 text-white focus:outline-none disabled:bg-indigo-400"
>
Save
</button>
</Form>
)}
</Formik>
</div>
</div>
);
}