assignment5 Blogpost code review #1

Merged
sumitdml123 merged 6 commits from dev into main 2025-01-14 11:35:16 +00:00
27 changed files with 18028 additions and 0 deletions

26
.gitignore vendored Normal file
View file

@ -0,0 +1,26 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*
App.css

17414
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

40
package.json Normal file
View file

@ -0,0 +1,40 @@
{
"name": "blog_app",
"version": "0.1.0",
"private": true,
"dependencies": {
"cra-template": "1.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router-dom": "^7.1.1",
"react-scripts": "5.0.1",
"web-vitals": "^4.2.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"prettier": "3.4.2"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

43
public/index.html Normal file
View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
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>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View file

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View file

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

13
src/App.js Normal file
View file

@ -0,0 +1,13 @@
import './App.css';
import React from 'react';
import Layout from './Component/Layout';
function App() {
return (
<div className="App">
<Layout />
</div>
);
}
export default App;

8
src/App.test.js Normal file
View file

@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

52
src/Component/Body.js Normal file
View file

@ -0,0 +1,52 @@
import React from "react";
import "../styleing/body.css";
import Pagination from "./Pagination";
import AddPostForm from "./addPost";
function Body({ filteredData, setFilteredData, currentPage, setCurrentPage }) {
const postsPerPage = 6;
const handleAddPost = (newPost) => {
const postWithId = { ...newPost, id: Date.now() };
const updatedData = [postWithId, ...filteredData];
setFilteredData(updatedData);
};
const handleDeletePost = (id) => {
const updatedData = filteredData?.filter((post) => post?.id !== id);
setFilteredData(updatedData);
};
const indexOfLastPost = currentPage * postsPerPage;
const indexOfFirstPost = indexOfLastPost - postsPerPage;
const currentPosts = filteredData?.slice(indexOfFirstPost, indexOfLastPost);
const totalPages = Math.ceil(filteredData?.length / postsPerPage);
return (
<div className="App">
<ul>
{filteredData?.length > 0 ? (
currentPosts?.map((post) => (
<li key={post?.id}>
<h1>Title: {post?.title}</h1>
<p>
<b>Content:</b> {post?.body}
</p>
<button onClick={() => handleDeletePost(post?.id)} >Delete</button>
</li>
))
) : (
<li>No posts found</li>
)}
</ul>
<AddPostForm onAddPost={handleAddPost} />
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={(page) => setCurrentPage(page)}
/>
</div>
);
}
export default Body;

21
src/Component/DarkMode.js Normal file
View file

@ -0,0 +1,21 @@
import React, { useState } from "react";
function DarkMode() {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
<div
style={{
backgroundColor: theme === "light" ? "#fff" : "#333",
color: theme === "light" ? "#000" : "#fff",
height: "100vh",
transition: "background-color 0.3s",
}}
>
<h1>{theme.charAt(0).toUpperCase() + theme.slice(1)}Mode</h1>
<button onClick={toggleTheme}> Toggle Theme</button>
</div>
);
}
export default DarkMode;

63
src/Component/Layout.js Normal file
View file

@ -0,0 +1,63 @@
import React, { useState, useEffect } from "react";
import Body from "./Body";
import SearchBar from "./SearchBar";
import useFetch from "../hooks/useFetch";
import "../styleing/layout.css";
veerjot_dml marked this conversation as resolved
Review

wrong way of importing env fix this

wrong way of importing env fix this
const my_key = process.env.REACT_APP_API_KEY;
const Layout = () => {
const [filteredData, setFilteredData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
veerjot_dml marked this conversation as resolved
Review

refactor code and make reusable function /hook

refactor code and make reusable function /hook
const { data, loading, error } = useFetch(my_key);
useEffect(() => {
if (data) {
setFilteredData(data);
}
}, [data]);
const handleSearch = (searchTerm) => {
if (!searchTerm) {
setFilteredData(data);
} else {
const filtered = data?.filter(
(post) =>
post?.title?.toLowerCase().includes(searchTerm?.toLowerCase()) ||
post?.body?.toLowerCase().includes(searchTerm?.toLowerCase())
);
if (filtered?.length > 0) {
setFilteredData(filtered);
} else {
setFilteredData([]);
}
veerjot_dml marked this conversation as resolved
Review

fix this

fix this
setCurrentPage(1);
}
};
if (loading) {
return <div>Loading....</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<div className="contentPage">
<div className="header">
<h2 className="heading">Latest Blogs</h2>
<SearchBar onSearch={handleSearch} />
</div>
<div className="body">
<Body
filteredData={filteredData}
setFilteredData={setFilteredData}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
/>
</div>
</div>
);
};
export default Layout;

View file

@ -0,0 +1,29 @@
import React from "react";
import "../styleing/pagination.css";
const Pagination = ({ currentPage, totalPages, onPageChange }) => {
const handlePageChange = (page) => {
onPageChange(page);
};
return (
<div className="pagination">
<button
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
>
Previous
</button>
<span>
Page {currentPage} of {totalPages}
</span>
<button
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
Next
</button>
</div>
);
};
export default Pagination;

View file

@ -0,0 +1,26 @@
import React, { useState } from "react";
import "../styleing/SearchBar.css";
const SearchBar = ({ onSearch }) => {
const [searchTerm, setSearchTerm] = useState("");
const handleInputChange = (event) => {
const value = event?.target?.value;
setSearchTerm(value);
onSearch(value);
};
return (
<div className="searchBar">
<input
type="text"
className="search"
value={searchTerm}
onChange={handleInputChange}
placeholder="Search for posts..."
/>
</div>
);
};
export default SearchBar;

37
src/Component/addPost.js Normal file
View file

@ -0,0 +1,37 @@
import React, { useState } from "react";
import "../styleing/addPost.css";
const AddPostForm = ({ onAddPost }) => {
const [newPost, setNewPost] = useState({ title: "", body: "" });
const handleSubmit = (e) => {
e.preventDefault();
if (newPost.title && newPost.body) {
onAddPost(newPost);
setNewPost({ title: "", body: "" });
} else {
alert("Title and Body are required!");
}
};
return (
<div className="addPostForm">
<input
type="text"
placeholder="Post Title"
value={newPost.title}
onChange={(e) => setNewPost((prevState)=>({ ...prevState, title: e.target.value }))}
/>
<textarea
placeholder="Post Content"
value={newPost.body}
onChange={(e) => setNewPost({ ...newPost, body: e.target.value })}
/>
<button onClick={handleSubmit}>Add Post</button>
</div>
);
};
export default AddPostForm;

28
src/hooks/useFetch.js Normal file
View file

@ -0,0 +1,28 @@
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const res = await fetch(url);
if (!res.ok) {
throw new Error("Failed to fetch data");
}
const result = await res.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
export default useFetch;

13
src/index.css Normal file
View file

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

17
src/index.js Normal file
View file

@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

13
src/reportWebVitals.js Normal file
View file

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.js Normal file
View file

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

View file

@ -0,0 +1,32 @@
:root {
--borderColor: #ccc;
--bgColor: #f9f9f9;
--bFocus: #4caf50;
}
.searchBar {
display: flex;
justify-content: space-between;
align-items: center;
margin: 20px 0;
}
.search {
padding: 10px;
font-size: 1.2em;
width: 300px;
border: 1px solid var(--borderColor);
border-radius: 4px;
background-color: var(--bgColor);
}
.search:focus {
border-color: var(--bFocus);
outline: none;
}
.search {
position: absolute;
margin-left: 50cap;
padding: 10px;
border: 1px solid var(black);
border-radius: 10px;
}

40
src/styleing/addPost.css Normal file
View file

@ -0,0 +1,40 @@
:root {
--borderColor: #ccc;
--bgColor: #f9f9f9;
--bgCol: #4caf50;
--bgColHover: #45a049;
}
.addPostForm {
display: flex;
flex-direction: column;
margin: 20px 0;
}
.addPostForm input,
.addPostForm textarea {
padding: 12px;
margin-bottom: 15px;
border: 1px solid var(--borderColor);
border-radius: 4px;
font-size: 1.1em;
width: 100%;
}
.addPostForm button {
padding: 12px;
background-color: var(--bgCol);
color: var(--bgColor);
font-size: 1.1em;
border: none;
border-radius: 4px;
cursor: pointer;
}
.addPostForm button:hover {
background-color: var(--bgColHover);
}
.addPostForm input:focus,
.addPostForm textarea:focus {
border-color: var(--bgCol);
}

27
src/styleing/body.css Normal file
View file

@ -0,0 +1,27 @@
:root {
--borderColor: black;
}
ul {
list-style-type: none;
display: grid;
grid-template-columns: auto auto auto;
gap: 20px;
}
li {
border: 2px solid var(--borderColor);
background-image: linear-gradient(rgba(135, 207, 235, 0.588),rgba(230, 116, 116, 0.295));
font-family: Georgia, 'Times New Roman', Times, serif;
border-radius: 15px;
padding: 5px;
}
button{
background-color: skyblue;
border-radius: 10px;
}
button:hover{
background-color: rgba(230, 116, 116, 0.632);
}

18
src/styleing/layout.css Normal file
View file

@ -0,0 +1,18 @@
:root {
--imgBG_LG1: skyblue;
--imgBG_LG2: rgb(230, 116, 116);
--headingColor: black;
}
.header {
background-image: linear-gradient(var(--imgBG_LG1), var(--imgBG_LG2));
height: auto;
width: 100%;
padding: 5px;
margin-top: -16px;
}
.heading {
text-align: center;
font-family: Arial, Helvetica, sans-serif;
font-size: xx-large;
color: var(--headingColor);
}

View file

@ -0,0 +1,35 @@
:root {
--border: #ccc;
--bgColor: #f9f9f9;
--bgHover: #ddd;
--paginationBG: #4caf50;
--paginationCol: white;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 20px;
}
.pagination button {
padding: 8px 12px;
margin: 0 5px;
border: 1px solid var(--border);
border-radius: 4px;
background-color: var(--bgColor);
cursor: pointer;
}
.pagination button:hover {
background-color: var(--bgHover);
}
.pagination .active {
background-color: var(--paginationBG);
color: var(--paginationCol);
}
.pagination .disabled {
background-color: var(--bgColor);
cursor: not-allowed;
}