├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── index.html └── netlify.toml ├── src ├── App.css ├── App.js ├── Components │ ├── Footer.jsx │ ├── Header.jsx │ ├── Loader.jsx │ └── RepoCard.jsx ├── Context │ └── ThemeContext.js ├── Pages │ ├── HomePage.jsx │ ├── MyProfile.jsx │ ├── NotFound.jsx │ └── Profile.jsx ├── index.css ├── index.js └── styles │ └── Loader.css └── tailwind.config.js /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Profile Finder 2 | 3 | - My personel react.js project 4 | 5 | * Click to view live demo 6 | (https://git-hub-finder-react.netlify.app) 7 | ## State Management Tool 8 | 9 | - Context api 10 | 11 | ## Dependencies 12 | 13 | - axios 14 | - react-router-dom v6 15 | - react-paginate 16 | 17 | ## Desing 18 | 19 | - tailwind.css 20 | - daisyui 21 | - react-icons 22 | - mobile hamburger menu 23 | - responsive design 24 | 25 | ## GitHub Api Source Link: 26 | 27 | (https://api.github.com) 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-finder-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.1", 7 | "@testing-library/react": "^12.1.2", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^0.24.0", 10 | "daisyui": "^1.20.0", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-icons": "^4.3.1", 14 | "react-paginate": "^8.1.0", 15 | "react-router-dom": "^6.2.1", 16 | "react-scripts": "5.0.0", 17 | "tailwind-scrollbar-hide": "^1.1.7", 18 | "web-vitals": "^2.1.2" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | }, 44 | "devDependencies": { 45 | "autoprefixer": "^10.4.0", 46 | "postcss": "^8.4.5", 47 | "tailwindcss": "^3.0.7" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | React App 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/" 4 | status = 200 -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | /* Theme Button Css Start */ 2 | .chkbx input[type="checkbox"] { 3 | display: none; 4 | } 5 | .chkbx { 6 | position: relative; 7 | cursor: pointer; 8 | } 9 | .chkbx .x { 10 | display: block; 11 | width: 50px; 12 | height: 25px; 13 | border: 2px solid #d3d3be; 14 | border-radius: 60px; 15 | transition: 0.5s; 16 | } 17 | .chkbx .x:before { 18 | content: ""; 19 | position: absolute; 20 | width: 17px; 21 | height: 17px; 22 | top: 4px; 23 | left: 5px; 24 | box-sizing: border-box; 25 | background: #d3d3be; 26 | border: 2px solid #d3d3be; 27 | border-radius: 40px; 28 | transition: 0.5s; 29 | } 30 | .chkbx :checked ~ .x:before { 31 | background: #161615; 32 | border-color: #fe7f2d; 33 | transform: translatex(22px); 34 | } 35 | .chkbx :checked ~ .x { 36 | border-color: #fe7f2d; 37 | } 38 | /* Theme Button Css End */ 39 | 40 | /* Pagination Css Start */ 41 | .pagination .btn { 42 | padding: 0px; 43 | margin: 5px 5px; 44 | } 45 | .page-link { 46 | padding: 16px 22px; 47 | border-radius: 8px; 48 | } 49 | 50 | /* Pagination Css End */ 51 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; 3 | 4 | //! COMPONENTS 5 | import Header from "./Components/Header"; 6 | import Footer from "./Components/Footer"; 7 | 8 | //! PAGES 9 | import HomePage from "./Pages/HomePage"; 10 | import Profile from "./Pages/Profile"; 11 | import NotFound from "./Pages/NotFound"; 12 | import MyProfile from "./Pages/MyProfile"; 13 | 14 | //! CONTEXT 15 | import { ThemeProvider } from "./Context/ThemeContext"; 16 | 17 | 18 | function App() { 19 | 20 | return ( 21 |
22 | 23 | 24 |
25 | 26 | } /> 27 | } /> 28 | } /> 29 | } /> 30 | } /> 31 | 32 |
36 | ); 37 | } 38 | 39 | export default App; 40 | -------------------------------------------------------------------------------- /src/Components/Footer.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { BsGithub } from "react-icons/bs"; 3 | import ThemeContext from "../Context/ThemeContext"; 4 | 5 | const Footer = () => { 6 | 7 | const { theme } = useContext(ThemeContext); 8 | 9 | return ( 10 | 14 | ); 15 | }; 16 | 17 | export default Footer; 18 | -------------------------------------------------------------------------------- /src/Components/Header.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { Link, NavLink } from "react-router-dom"; 3 | import { BsGithub } from "react-icons/bs"; 4 | import { AiOutlineMenu } from "react-icons/ai" 5 | import ThemeContext from "../Context/ThemeContext"; 6 | 7 | const Header = () => { 8 | const { theme, setTheme } = useContext(ThemeContext); 9 | 10 | //! Mobile Menu Functions 11 | const mobileMenu = () => { document.querySelector('#mobile-menu').classList.toggle('hidden') } 12 | const mobileMenuClose = () => { document.querySelector('#mobile-menu').classList.add('hidden') } 13 | 14 | return ( 15 |
16 | {/* Logo */} 17 |
18 | 19 | 20 | 21 |

22 | Github Profile Finder 23 |

24 |
25 | 26 | {/* Desktop Navbar */} 27 | 53 | 54 | {/* Mobile Right Bar */} 55 |
56 | {/* Mobile Theme Button */} 57 | 64 | {/* Mobile Hamburger Btn */} 65 | 68 |
69 | 70 | {/* Mobile Menu */} 71 | 88 |
89 | ); 90 | }; 91 | 92 | export default Header; 93 | -------------------------------------------------------------------------------- /src/Components/Loader.jsx: -------------------------------------------------------------------------------- 1 | import "../styles/Loader.css"; 2 | 3 | const Loader = () => { 4 | return ( 5 | <> 6 |
7 |
8 |
9 |
10 |
11 |
12 | 13 | ); 14 | }; 15 | 16 | export default Loader; 17 | -------------------------------------------------------------------------------- /src/Components/RepoCard.jsx: -------------------------------------------------------------------------------- 1 | 2 | const RepoCard = ({ liveDemo, name, description, topics, htmlUrl, language, }) => { 3 | 4 | return ( 5 | <> 6 | {/* */} 7 |
8 | 9 | {/* */} 10 |
11 | {/* */} 12 |
13 | {/* */} 14 |
15 |

16 | {name} 17 | 18 | {language ? language : "Readme"} 19 | 20 |

21 |

{description}

22 |

23 | {topics.map((topic) => ( 24 | 25 | {topic} 26 | 27 | ))} 28 |

29 |
30 | 31 | 32 | View Repo 33 | 34 | {liveDemo && 35 | Live Demo 36 | } 37 |
38 | 39 | ); 40 | }; 41 | 42 | export default RepoCard; 43 | -------------------------------------------------------------------------------- /src/Context/ThemeContext.js: -------------------------------------------------------------------------------- 1 | import { createContext, useState } from "react"; 2 | 3 | const ThemeContext = createContext(); 4 | 5 | export const ThemeProvider = ({ children }) => { 6 | const [theme, setTheme] = useState("dark"); 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default ThemeContext; 16 | -------------------------------------------------------------------------------- /src/Pages/HomePage.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { BsGithub } from "react-icons/bs"; 4 | import axios from "axios"; 5 | import ReactPaginate from "react-paginate"; 6 | import Loader from "../Components/Loader"; 7 | 8 | 9 | const HomePage = () => { 10 | 11 | //! USE STATES 12 | const [input, setInput] = useState(""); 13 | const [userSearch, setUserSearch] = useState([]); 14 | const [loading, setLoading] = useState(false) 15 | const [totalItemCount, setTotalItemCount] = useState([]) 16 | 17 | //! SEARCH USER FUNCTION 18 | const onSubmitHandler = (e) => { 19 | e.preventDefault(); 20 | setLoading(true) 21 | setTimeout(() => { 22 | axios.get(`https://api.github.com/search/users?q=${input}`).then(res => { setUserSearch(res.data.items); setTotalItemCount(res.data) }) 23 | setLoading(false) 24 | console.log(userSearch); 25 | }, 1200); 26 | } 27 | 28 | //! CHANGE PAGINATION SEARCH USER 29 | const handlePageClick = (data) => { 30 | let currentPage = data.selected + 1; 31 | axios.get(`https://api.github.com/search/users?q=${input}&page=${currentPage}`).then(res => setUserSearch(res.data.items)) 32 | } 33 | 34 | return ( 35 | <> 36 | 37 | {/* Title */} 38 |

Search GitHub Profile

39 | 40 | {/* Search Bar */} 41 |
42 |
43 | { setInput(e.target.value); setUserSearch([]) }} type="search" placeholder="Search" className="w-4/6 input input-primary input-bordered lg:w-2/6" /> 44 | 45 |
46 |
47 | 48 | {/* Pagination Bar */} 49 |
50 | {userSearch.length ? 51 | '} 55 | breakLabel={'...'} 56 | pageCount={totalItemCount.total_count ? (totalItemCount.total_count > 999 ? 32 : Math.ceil(totalItemCount.total_count / 30)) : 0} 57 | marginPagesDisplayed={3} 58 | pageRangeDisplayed={1} 59 | onPageChange={handlePageClick} 60 | containerClassName={'btn-group flex justify-center p-5'} 61 | pageClassName={'btn'} 62 | pageLinkClassName={'page-link'} 63 | previousClassName={'btn btn-primary'} 64 | previousLinkClassName={'page-link'} 65 | nextClassName={'btn btn-primary'} 66 | nextLinkClassName={'page-link'} 67 | breakClassName={'btn'} 68 | breakLinkClassName={'page-link'} 69 | activeClassName={'btn-active'} 70 | /> : ""} 71 |
72 | 73 | {/* USER LIST CONTAINER*/} 74 |
75 | {/* USER LIST */} 76 | {loading ? : userSearch.map(user => { 77 | return
78 | {/* USER CARD */} 79 |
80 | {/* CARD AVATAR */} 81 |
82 |
83 | avatar 84 |
85 |
86 | {/* CARD BODY */} 87 |
88 |

{user.login}

89 |
90 |

{user.location}

91 |
92 | 97 |
98 | 99 | 100 | View Profile 101 | 102 |
103 |
104 |
105 |
106 | })} 107 |
108 | 109 | ); 110 | }; 111 | 112 | export default HomePage; 113 | -------------------------------------------------------------------------------- /src/Pages/MyProfile.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { BsGithub, BsLinkedin } from "react-icons/bs"; 3 | import { GoLocation } from "react-icons/go"; 4 | import axios from "axios"; 5 | import Loader from "../Components/Loader"; 6 | import RepoCard from "../Components/RepoCard"; 7 | 8 | 9 | const MyProfile = () => { 10 | 11 | const [data, setData] = useState([]); 12 | const [repos, setRepos] = useState([]); 13 | 14 | useEffect(() => { 15 | const apiCall = setTimeout(() => { 16 | axios.get("https://api.github.com/users/emrekaraa").then((res) => setData(res.data)); 17 | axios.get("https://api.github.com/users/emrekaraa/repos").then((res) => setRepos(res.data)); 18 | }, 1500); 19 | return () => clearTimeout(apiCall); 20 | }, []); 21 | 22 | return ( 23 | <> 24 | {/* Container */} 25 |
26 | {/* Page title */} 27 |

28 | My GitHub Profile 29 |

30 | {/* Loader animation data control */} 31 | {data.length !== 0 ? 32 | <> 33 | {/* Profile Card */} 34 |
35 | {/* Card Avatar */} 36 |
37 |
38 | avatar 39 |
40 |
41 | 42 | {/* Card Body */} 43 |
44 |

{data.name}

45 | 62 |
63 | 64 |

{data.location}

65 |
66 | 72 | 78 |
79 |
80 | 81 | {/* Repositories */} 82 |
83 |

Repositories

84 |

({repos.length})

85 |
86 | {repos.map((repo, i) => ( 87 | 96 | ))} 97 |
98 | {data.public_repos > 30 && View all repos} 99 |
100 | : } 101 |
102 | 103 | ); 104 | }; 105 | 106 | export default MyProfile; 107 | -------------------------------------------------------------------------------- /src/Pages/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import { ImHeartBroken } from "react-icons/im"; 2 | import { Link } from "react-router-dom"; 3 | 4 | const NotFound = () => { 5 | return ( 6 |
7 | 8 |
9 |

Uppps... Worng Path

10 |

404 Page Not Found !

11 | 12 | Return to HomePage 13 | 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default NotFound; 20 | -------------------------------------------------------------------------------- /src/Pages/Profile.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import { BsGithub, BsLinkedin } from "react-icons/bs"; 4 | import { GoLocation } from "react-icons/go"; 5 | import axios from "axios"; 6 | import Loader from "../Components/Loader"; 7 | import RepoCard from "../Components/RepoCard"; 8 | 9 | const Profile = () => { 10 | let { username } = useParams(); 11 | 12 | const [data, setData] = useState([]); 13 | const [repos, setRepos] = useState([]); 14 | 15 | useEffect(() => { 16 | const apiCall = setTimeout(() => { 17 | axios.get(`https://api.github.com/users/${username}`).then((res) => setData(res.data)); 18 | axios.get(`https://api.github.com/users/${username}/repos`).then((res) => setRepos(res.data)); 19 | }, 1500); 20 | return () => clearTimeout(apiCall); 21 | }, []); 22 | 23 | return ( 24 | <> 25 | {/* Container */} 26 |
27 | {/* Page title */} 28 |

29 | {username} GitHub Profile 30 |

31 | {/* Loader animation data control */} 32 | {data.length !== 0 ? ( 33 | <> 34 | {/* Profile Card */} 35 |
36 | {/* Card Avatar */} 37 |
38 |
39 | avatar 40 |
41 |
42 | 43 | {/* Card Body */} 44 |
45 |

{data.name ? data.name : username}

46 | 63 |
64 | {data.location && } 65 | 66 |

{data.location}

67 |
68 |
69 | {data.blog && } 70 | 71 | {data.blog} 72 | 73 |
74 | 80 |
81 |
82 | 83 | {/* Repositories */} 84 |
85 |

Repositories

86 |

({data.public_repos})

87 |
88 | {repos.map((repo, i) => ( 89 | 98 | ))} 99 |
100 | {data.public_repos > 30 && View all repos} 101 |
102 | 103 | ) : ()} 104 |
105 | 106 | ) 107 | } 108 | 109 | export default Profile 110 | 111 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById("root") 11 | ); 12 | -------------------------------------------------------------------------------- /src/styles/Loader.css: -------------------------------------------------------------------------------- 1 | .lds-ring { 2 | display: inline-block; 3 | position: relative; 4 | width: 80px; 5 | height: 80px; 6 | } 7 | .lds-ring div { 8 | box-sizing: border-box; 9 | display: block; 10 | position: absolute; 11 | width: 64px; 12 | height: 64px; 13 | margin: 8px; 14 | border: 8px solid #fff; 15 | border-radius: 50%; 16 | animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; 17 | border-color: #fff transparent transparent transparent; 18 | } 19 | .lds-ring div:nth-child(1) { 20 | animation-delay: -0.45s; 21 | } 22 | .lds-ring div:nth-child(2) { 23 | animation-delay: -0.3s; 24 | } 25 | .lds-ring div:nth-child(3) { 26 | animation-delay: -0.15s; 27 | } 28 | @keyframes lds-ring { 29 | 0% { 30 | transform: rotate(0deg); 31 | } 32 | 100% { 33 | transform: rotate(360deg); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./src/**/*.{js,jsx,ts,tsx}"], 3 | theme: { 4 | fontSize: { 5 | xs: ".75rem", 6 | sm: ".875rem", 7 | tiny: ".875rem", 8 | base: "1rem", 9 | lg: "1.125rem", 10 | xl: "1.25rem", 11 | "2xl": "1.5rem", 12 | "3xl": "1.875rem", 13 | "4xl": "2.25rem", 14 | "5xl": "3rem", 15 | "6xl": "4rem", 16 | "7xl": "5rem", 17 | "10xl": "10rem", 18 | }, 19 | extend: {}, 20 | }, 21 | plugins: [require("daisyui"), require("tailwind-scrollbar-hide")], 22 | }; 23 | --------------------------------------------------------------------------------