├── src ├── screens │ ├── home │ │ ├── Home.css │ │ └── Home.js │ ├── editpost │ │ ├── Editpost.css │ │ └── Editpost.js │ ├── createpost │ │ ├── Createpost.css │ │ └── Createpost.js │ └── postdetail │ │ ├── PostDetail.css │ │ └── PostDetail.js ├── components │ ├── switch │ │ ├── ThemeSwitch.css │ │ └── ThemeSwitch.js │ ├── appsubmitbutton │ │ ├── Appsubmitbutton.css │ │ └── Appsubmitbutton.js │ ├── post │ │ ├── Post.css │ │ └── Post.js │ └── navbar │ │ ├── Navbar.css │ │ └── Navbar.js ├── index.css ├── hooks │ ├── useThemeContext.js │ └── useFetch.js ├── index.js ├── context │ └── ThemeContext.js └── App.js ├── public ├── robots.txt ├── manifest.json └── index.html ├── .gitignore ├── package.json └── README.md /src/screens/home/Home.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/screens/editpost/Editpost.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/switch/ThemeSwitch.css: -------------------------------------------------------------------------------- 1 | 2 | .toggle { 3 | margin: 20px !important; 4 | } -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/screens/createpost/Createpost.css: -------------------------------------------------------------------------------- 1 | 2 | .outercontainer { 3 | margin-top: 70px; 4 | } 5 | 6 | .form-group { 7 | padding: 10px !important; 8 | } -------------------------------------------------------------------------------- /src/screens/postdetail/PostDetail.css: -------------------------------------------------------------------------------- 1 | 2 | .outer { 3 | margin-top: 50px; 4 | margin-left: 10px; 5 | } 6 | 7 | .btn { 8 | margin: 10px !important; 9 | } 10 | -------------------------------------------------------------------------------- /src/components/appsubmitbutton/Appsubmitbutton.css: -------------------------------------------------------------------------------- 1 | 2 | .lightbtn { 3 | background-color:#a86c4e !important; 4 | } 5 | 6 | .darkbtn { 7 | background-color:#5a77bb !important; 8 | } -------------------------------------------------------------------------------- /src/components/post/Post.css: -------------------------------------------------------------------------------- 1 | 2 | .card { 3 | margin: 10px !important; 4 | /* background-color:#F2e9e6; */ 5 | } 6 | 7 | .lightcard { 8 | background-color:#f2e9e6 !important; 9 | } 10 | 11 | .darkcard { 12 | background-color:#c3cee5 !important; 13 | 14 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | /* background: #EEE2DC; */ 4 | 5 | } 6 | 7 | code { 8 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 9 | monospace; 10 | } 11 | 12 | .btn-primary, .btn-primary:hover, .btn-primary:active, .btn-primary:visited { 13 | background-color:#a86c4e !important; 14 | } 15 | 16 | #root { 17 | height: 100vh; 18 | } -------------------------------------------------------------------------------- /src/hooks/useThemeContext.js: -------------------------------------------------------------------------------- 1 | 2 | import { useContext } from 'react'; 3 | import { ThemeContext } from './../context/ThemeContext'; 4 | 5 | export const useThemeContext = () => { 6 | 7 | const themeContext = useContext(ThemeContext) 8 | 9 | 10 | if (themeContext === undefined) { 11 | throw new Error('Theme context is undefined') 12 | } 13 | 14 | return themeContext 15 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { ThemeContextProvider } from './context/ThemeContext'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/appsubmitbutton/Appsubmitbutton.js: -------------------------------------------------------------------------------- 1 | import './Appsubmitbutton.css' 2 | import React from 'react' 3 | import { useThemeContext } from './../../hooks/useThemeContext'; 4 | 5 | export default function Appsubmitbutton({onClick,title}) { 6 | 7 | const {theme} = useThemeContext() 8 | return ( 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/components/navbar/Navbar.css: -------------------------------------------------------------------------------- 1 | 2 | .lightheader{ 3 | background: #EDC7B7; 4 | } 5 | 6 | .darkheader { 7 | background:#5a77bb; 8 | } 9 | 10 | header .container { 11 | max-width: 1200px; 12 | margin: 0 auto; 13 | padding: 10px 20px; 14 | display: flex; 15 | align-items: center; 16 | justify-content:space-between; 17 | } 18 | 19 | header a { 20 | color:#333; 21 | text-decoration:none 22 | } 23 | 24 | nav { 25 | display:flex; 26 | align-items: center; 27 | } 28 | 29 | nav a { 30 | margin-left: 20px; 31 | } -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | BLOG 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/screens/home/Home.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Post from "../../components/post/Post"; 3 | import "./Home.css"; 4 | import { useFetch } from './../../hooks/useFetch'; 5 | 6 | export default function Home() { 7 | 8 | const {data : posts,error,isPending} = useFetch("https://jsonplaceholder.typicode.com/posts") 9 | 10 | return (
11 | { 12 | posts && posts.map((post) => { 13 | return 14 | }) 15 | } 16 | { 17 | error &&

{error}

18 | } 19 | { 20 | isPending &&

Loading...

21 | } 22 | 23 |
) 24 | } 25 | -------------------------------------------------------------------------------- /src/components/navbar/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./Navbar.css"; 3 | import { Link } from "react-router-dom"; 4 | import { useThemeContext } from './../../hooks/useThemeContext'; 5 | 6 | export default function Navbar() { 7 | 8 | const {theme} = useThemeContext() 9 | 10 | return ( 11 |
12 |
13 | 14 |

Blog

15 | 16 | 24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/components/post/Post.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | import "./Post.css"; 4 | import { useThemeContext } from './../../hooks/useThemeContext'; 5 | 6 | export default function Post({post}) { 7 | 8 | const {theme} = useThemeContext() 9 | 10 | const navigate = useNavigate() 11 | 12 | 13 | const handleClick = () => { 14 | navigate(`/post/${post.id}`,{state:post}) 15 | } 16 | 17 | return ( 18 |
19 |
{post.title}
20 |
21 |

22 | {post.body} 23 |

24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/context/ThemeContext.js: -------------------------------------------------------------------------------- 1 | 2 | import { createContext } from 'react'; 3 | import { useReducer } from 'react'; 4 | 5 | 6 | export const ThemeContext = createContext() 7 | 8 | const ThemesReducer = (state,action) => { 9 | switch (action.type) { 10 | case 'LIGHT': 11 | return {...state,theme:'light'} 12 | case 'DARK': 13 | return {...state,theme:'dark'} 14 | default: 15 | return state 16 | } 17 | } 18 | 19 | 20 | export const ThemeContextProvider = ({children}) => { 21 | 22 | const [state,dispatch] = useReducer(ThemesReducer,{theme : 'light'}) 23 | return ( 24 | 25 | {children} 26 | 27 | 28 | ) 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/components/switch/ThemeSwitch.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import './Themeswitch.css'; 4 | import { useThemeContext } from './../../hooks/useThemeContext'; 5 | 6 | export default function ThemeSwitch() { 7 | 8 | 9 | const {theme,dispatch} = useThemeContext() 10 | 11 | 12 | const switchTheme = () => { 13 | if (theme === 'light') { 14 | dispatch({type:'DARK'}) 15 | } else { 16 | dispatch({type:'LIGHT'}) 17 | } 18 | console.log(theme) 19 | } 20 | 21 | return ( 22 |
23 |
24 | 30 |
31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blog", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.3.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-router-dom": "^6.3.0", 12 | "react-scripts": "^3.0.1", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { BrowserRouter, Route, Routes } from "react-router-dom"; 2 | import Navbar from "./components/navbar/Navbar"; 3 | import Home from "./screens/home/Home"; 4 | import Createpost from "./screens/createpost/Createpost"; 5 | import Postdetail from "./screens/postdetail/Postdetail"; 6 | import Editpost from "./screens/editpost/Editpost"; 7 | import Themeswitch from "./components/switch/Themeswitch"; 8 | 9 | import { useThemeContext } from "./hooks/useThemeContext"; 10 | 11 | function App() { 12 | const { theme } = useThemeContext(); 13 | 14 | return ( 15 |
16 | 17 | 18 | 19 |
20 | 21 | } /> 22 | } /> 23 | } /> 24 | } /> 25 | 26 |
27 |
28 |
29 | ); 30 | } 31 | 32 | export default App; 33 | -------------------------------------------------------------------------------- /src/hooks/useFetch.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | export const useFetch = (url, method = "GET") => { 4 | const [data, setData] = useState([]); 5 | const [error, setError] = useState(null); 6 | const [isPending, setIsPending] = useState(false); 7 | const [options, setOptions] = useState(null); 8 | 9 | const optionsData = (data) => { 10 | if (method === "POST") { 11 | setOptions({ 12 | method: "POST", 13 | body: JSON.stringify(data), 14 | headers: { 15 | "Content-type": "application/json; charset=UTF-8", 16 | }, 17 | }); 18 | } else if (method === "PATCH") { 19 | setOptions({ 20 | method: "PATCH", 21 | body: JSON.stringify(data), 22 | headers: { 23 | "Content-type": "application/json; charset=UTF-8", 24 | }, 25 | }); 26 | } else if (method === "DELETE") { 27 | setOptions({ 28 | method: "DELETE" 29 | }); 30 | } 31 | }; 32 | 33 | useEffect(() => { 34 | const fetchPosts = async (options) => { 35 | setIsPending(true); 36 | const response = await fetch(url, { ...options }); 37 | 38 | const jsonResponse = await response.json(); 39 | 40 | if (response.ok) { 41 | setData(jsonResponse); 42 | setError(""); 43 | setIsPending(false); 44 | } 45 | 46 | if (!response.ok) { 47 | setError(jsonResponse.error); 48 | setIsPending(false); 49 | } 50 | }; 51 | if (method === "GET") { 52 | fetchPosts(); 53 | } else if ((method === "POST" || method === "PATCH" || method === "DELETE") && options) { 54 | fetchPosts(options); 55 | } 56 | }, [url, method, options]); 57 | 58 | return { data, error, isPending, optionsData }; 59 | }; 60 | -------------------------------------------------------------------------------- /src/screens/postdetail/PostDetail.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import "./Postdetail.css"; 3 | import { useLocation, useNavigate } from "react-router-dom"; 4 | import { useFetch } from "./../../hooks/useFetch"; 5 | import Appsubmitbutton from "../../components/appsubmitbutton/Appsubmitbutton"; 6 | 7 | export default function Postdetail() { 8 | const location = useLocation(); 9 | 10 | const { state: post } = location; 11 | 12 | const { data, error, optionsData } = useFetch( 13 | `https://jsonplaceholder.typicode.com/posts/${post.id}`, 14 | "DELETE" 15 | ); 16 | 17 | const navigate = useNavigate(); 18 | 19 | const handleEdit = () => { 20 | navigate(`/edit/${post.id}`, { state: post }); 21 | }; 22 | 23 | const handleDelete = () => { 24 | optionsData(); 25 | }; 26 | 27 | useEffect(() => { 28 | if (data.length !== 0) { 29 | const timer = setTimeout(() => navigate("/"), 3000); 30 | return () => clearTimeout(timer); 31 | } 32 | }, [data, navigate]); 33 | 34 | return ( 35 |
36 |
37 |

{post.title}

38 |

{post.body}

39 | {data.length !== 0 && ( 40 |
41 | Post Deleted Successfully! 42 |
43 | )} 44 | {error && ( 45 |
46 | {error} 47 |
48 | )} 49 |
50 | 51 |
52 | 53 |
54 |
55 |
56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /src/screens/createpost/Createpost.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./Createpost.css"; 3 | import { useState,useEffect } from "react"; 4 | import { useFetch } from './../../hooks/useFetch'; 5 | import { useNavigate } from 'react-router-dom'; 6 | import Appsubmitbutton from "../../components/appsubmitbutton/Appsubmitbutton"; 7 | 8 | export default function Createpost() { 9 | const [title, setTitle] = useState(""); 10 | const [content, setContent] = useState(""); 11 | const [validationError, setValidationError] = useState(""); 12 | 13 | const navigate = useNavigate() 14 | 15 | const {data, error,optionsData} = useFetch('https://jsonplaceholder.typicode.com/posts',"POST") 16 | 17 | const handleSubmit = (e) => { 18 | e.preventDefault(); 19 | 20 | if (!title) { 21 | setValidationError("Title should not be empty"); 22 | return 23 | } 24 | if (!content) { 25 | setValidationError("Content should not be empty"); 26 | return 27 | } 28 | setValidationError(""); 29 | console.log({ title, body: content, userId:1}); 30 | optionsData({ title, body: content, userId:1}) 31 | }; 32 | 33 | useEffect(() => { 34 | if (data.length !== 0) { 35 | const timer = setTimeout(() => navigate("/"),3000); 36 | return () => clearTimeout(timer) 37 | } 38 | 39 | },[data,navigate]) 40 | 41 | return ( 42 |
43 |
44 |
45 | 48 | setTitle(e.target.value)} 53 | /> 54 |
55 |
56 | 59 |