├── 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 |

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 |
--------------------------------------------------------------------------------