├── .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 |
33 |
34 |
35 |
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 |
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 |
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 |
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 |

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 |

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 |

40 |
41 |
42 |
43 | {/* Card Body */}
44 |
45 |
{data.name ? data.name : username}
46 |
63 |
64 | {data.location &&
}
65 |
66 |
{data.location}
67 |
68 |
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 |
--------------------------------------------------------------------------------