├── src ├── App.css ├── assets │ ├── images │ │ ├── 1.jpg │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.jpg │ │ ├── boy1.png │ │ ├── boy2.png │ │ ├── girl1.jpg │ │ ├── girl2.jpg │ │ └── profile.png │ └── react.svg ├── index.css ├── main.jsx ├── components │ ├── Header │ │ └── Header.jsx │ ├── Bookmark │ │ └── Bookmark.jsx │ ├── Bookmarks │ │ └── Bookmarks.jsx │ ├── Blogs │ │ └── Blogs.jsx │ └── Blog │ │ └── Blog.jsx └── App.jsx ├── public ├── CNAME ├── vite.svg └── blogs.json ├── postcss.config.js ├── vite.config.js ├── tailwind.config.js ├── .gitignore ├── index.html ├── README.md ├── .eslintrc.cjs └── package.json /src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/CNAME: -------------------------------------------------------------------------------- 1 | itchy-root.surge.sh -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/1.jpg -------------------------------------------------------------------------------- /src/assets/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/2.png -------------------------------------------------------------------------------- /src/assets/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/3.png -------------------------------------------------------------------------------- /src/assets/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/4.jpg -------------------------------------------------------------------------------- /src/assets/images/boy1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/boy1.png -------------------------------------------------------------------------------- /src/assets/images/boy2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/boy2.png -------------------------------------------------------------------------------- /src/assets/images/girl1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/girl1.jpg -------------------------------------------------------------------------------- /src/assets/images/girl2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/girl2.jpg -------------------------------------------------------------------------------- /src/assets/images/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammingHero1/B9-react-knowledge-cafe/HEAD/src/assets/images/profile.png -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Exo+2:wght@400;600;700&display=swap'); 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | body{ 7 | font-family: 'Exo 2', sans-serif; 8 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | "./index.html", 5 | "./src/**/*.{js,ts,jsx,tsx}", 6 | ], 7 | theme: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import profile from '../../assets/images/profile.png' 2 | const Header = () => { 3 | return ( 4 |
5 |

Knowledge Cafe

6 | 7 |
8 | ); 9 | }; 10 | 11 | export default Header; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/Bookmark/Bookmark.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | const Bookmark = ({bookmark}) => { 4 | const {title} = bookmark; 5 | return ( 6 |
7 |

{title}

8 |
9 | ); 10 | }; 11 | 12 | Bookmark.propTypes = { 13 | bookmark: PropTypes.object 14 | } 15 | 16 | export default Bookmark; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Bookmarks/Bookmarks.jsx: -------------------------------------------------------------------------------- 1 | 2 | import PropTypes from 'prop-types' 3 | import Bookmark from '../Bookmark/Bookmark' 4 | 5 | const Bookmarks = ({bookmarks, readingTime}) => { 6 | return ( 7 |
8 |
9 |

Reading Time: {readingTime}

10 |
11 |

Bookmarked Blogs: {bookmarks.length}

12 | { 13 | bookmarks.map((bookmark, idx) => ) 14 | } 15 |
16 | ) 17 | } 18 | 19 | Bookmarks.propTypes = { 20 | bookmarks: PropTypes.array, 21 | readingTime: PropTypes.number 22 | } 23 | 24 | export default Bookmarks -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-knowledge-cafe", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "prop-types": "^15.8.1", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "react-icons": "^4.11.0" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^18.2.15", 20 | "@types/react-dom": "^18.2.7", 21 | "@vitejs/plugin-react": "^4.0.3", 22 | "autoprefixer": "^10.4.15", 23 | "eslint": "^8.45.0", 24 | "eslint-plugin-react": "^7.32.2", 25 | "eslint-plugin-react-hooks": "^4.6.0", 26 | "eslint-plugin-react-refresh": "^0.4.3", 27 | "postcss": "^8.4.29", 28 | "tailwindcss": "^3.3.3", 29 | "vite": "^4.4.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/Blogs/Blogs.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useState } from "react"; 3 | import Blog from "../Blog/Blog"; 4 | import PropTypes from 'prop-types'; 5 | 6 | 7 | const Blogs = ({ handleAddToBookmark, handleMarkAsRead }) => { 8 | const [blogs, setBlogs] = useState([]); 9 | 10 | useEffect(() => { 11 | fetch('blogs.json') 12 | .then(res => res.json()) 13 | .then(data => setBlogs(data)) 14 | }, []) 15 | 16 | return ( 17 |
18 |

Blogs: {blogs.length}

19 | { 20 | blogs.map(blog => ) 26 | } 27 |
28 | ); 29 | }; 30 | 31 | Blogs.propTypes = { 32 | handleAddToBookmark: PropTypes.func, 33 | handleMarkAsRead: PropTypes.func 34 | } 35 | 36 | export default Blogs; -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import './App.css' 3 | import Blogs from './components/Blogs/Blogs' 4 | import Bookmarks from './components/Bookmarks/Bookmarks' 5 | import Header from './components/Header/Header' 6 | 7 | function App() { 8 | const [bookmarks, setBookmarks] = useState([]); 9 | const [readingTime, setReadingTime] = useState(0) 10 | 11 | const handleAddToBookmark = blog => { 12 | const newBookmarks = [...bookmarks, blog]; 13 | setBookmarks(newBookmarks); 14 | } 15 | 16 | const handleMarkAsRead = (id, time) =>{ 17 | const newReadingTime = readingTime + time; 18 | setReadingTime(newReadingTime); 19 | // remove the read blog from bookmark 20 | // console.log('remove bookmark', id) 21 | const remainingBookmarks = bookmarks.filter(bookmark => bookmark.id !== id); 22 | setBookmarks(remainingBookmarks); 23 | } 24 | 25 | return ( 26 | <> 27 |
28 |
29 | 30 | 31 |
32 | 33 | 34 | ) 35 | } 36 | 37 | export default App 38 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/blogs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "cover": "https://i.ibb.co/84Zfw2n/1.jpg", 5 | "title": "Top 10 ES6 Features You Must Know", 6 | "author_img": "https://i.ibb.co/VvLNdLL/boy1.png", 7 | "author": "Hamza Sohail", 8 | "posted_date": "September 15, 2023", 9 | "reading_time": 5, 10 | "hashtags": [ 11 | "beginners", 12 | "es6" 13 | ] 14 | }, 15 | { 16 | "id": 2, 17 | "cover": "https://i.ibb.co/6N2dgnZ/2.png", 18 | "title": "JavaScript Objects for Absolute Beginners: A Fun Introduction", 19 | "author_img": "https://i.ibb.co/7rFSJx5/girl1.jpg", 20 | "author": "Sajal Ali", 21 | "posted_date": "August 28, 2023", 22 | "reading_time": 4, 23 | "hashtags": [ 24 | "javascript", 25 | "coding", 26 | "dev" 27 | ] 28 | }, 29 | { 30 | "id": 3, 31 | "cover": "https://i.ibb.co/RzGzcPg/3.png", 32 | "title": "Component lifecycle in React — Class component vs Functional component", 33 | "author_img": "https://i.ibb.co/vwGwnhT/boy2.jpg", 34 | "author": "Feroze Khan", 35 | "posted_date": "July 10, 2023", 36 | "reading_time": 10, 37 | "hashtags": [ 38 | "mid_level", 39 | "react" 40 | ] 41 | }, 42 | { 43 | "id": 4, 44 | "cover": "https://i.ibb.co/vvTfkcv/4.jpg", 45 | "title": "Free images and resources collection for website", 46 | "author_img": "https://i.ibb.co/Sn7zB4q/girl2.jpg", 47 | "author": "Anmol Baloch", 48 | "posted_date": "June 5, 2023", 49 | "reading_time": 9, 50 | "hashtags": [ 51 | "development", 52 | "resources" 53 | ] 54 | } 55 | ] -------------------------------------------------------------------------------- /src/components/Blog/Blog.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { FaBookmark } from 'react-icons/fa'; 3 | 4 | const Blog = ({ blog, handleAddToBookmark, handleMarkAsRead }) => { 5 | const { id, title, cover, author, author_img, posted_date, reading_time, hashtags } = blog; 6 | return ( 7 |
8 | {`Cover 9 |
10 |
11 | 12 |
13 |

{author}

14 |

{posted_date}

15 |
16 |
17 |
18 | {reading_time} min read 19 | 23 |
24 |
25 |

{title}

26 |

27 | { 28 | hashtags.map((hash, idx) => #{hash} ) 29 | } 30 |

31 | 35 |
36 | ); 37 | }; 38 | 39 | Blog.propTypes = { 40 | blog: PropTypes.object.isRequired, 41 | handleAddToBookmark: PropTypes.func, 42 | handleMarkAsRead: PropTypes.func 43 | } 44 | 45 | export default Blog; -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------