├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── _redirects ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── App.test.js ├── components ├── Card.js ├── Charts │ ├── Bar.js │ ├── Column.js │ ├── Doughnut.js │ ├── Pie.js │ └── index.js ├── Followers.js ├── Info.js ├── Navbar.js ├── Repos.js ├── Search.js ├── User.js └── index.js ├── context ├── context.js └── mockData.js │ ├── mockFollowers.js │ ├── mockRepos.js │ └── mockUser.js ├── images ├── login-img.svg └── preloader.gif ├── index.css ├── index.js ├── pages ├── AuthWrapper.js ├── Dashboard.js ├── Error.js ├── Login.js ├── PrivateRoute.js └── index.js ├── serviceWorker.js └── setupTests.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 | ## Starter Project 2 | 3 | - css provided (global styles, styled components) 4 | - folders/files already setup 5 | - all imports included (warnings) 6 | - index.js for easier imports 7 | 8 | ## Styled Components 9 | 10 | [Styled-Components - Main Docs](https://styled-components.com/) 11 | 12 | ```jsx 13 | import styled from "styled-components"; 14 | 15 | const ReactComponent = () => { 16 | // logic here 17 | return 18 | {some content} 19 | 20 | } 21 | 22 | 23 | const Wrapper = styled.htmlElement` 24 | write your styles here 25 | ` 26 | export default ReactComponent 27 | ``` 28 | 29 | ## React Icons 30 | 31 | [React Icons - Main Docs](https://react-icons.github.io/react-icons/) 32 | 33 | ```jsx 34 | import { FiUsers, FiUserPlus } from 'react-icons/fi'; 35 | ; 36 | ``` 37 | 38 | ## React Router Dom 39 | 40 | version used - "react-router-dom": "^5.2.0", 41 | 42 | - [react-router-dom - Main Docs](https://reactrouter.com/web/guides/quick-start) 43 | 44 | - renders the first child that matches 45 | - A always matches 46 | 47 | ## Gihthub API 48 | 49 | - [Root Endpoint](https://api.github.com) 50 | - [Get User](https://api.github.com/users/wesbos) 51 | - [Repos](https://api.github.com/users/john-smilga/repos?per_page=100) 52 | - [Followers](https://api.github.com/users/john-smilga/followers) 53 | - [Rate Limit](https://api.github.com/rate_limit) 54 | 55 | For unauthenticated requests, the rate limit allows for up to 60 requests per hour. Unauthenticated requests are associated with the originating IP address, and not the user making requests. 56 | 57 | ## Fusion Charts 58 | 59 | - [Fusion Charts - Main Docs](https://www.fusioncharts.com/) 60 | - [First React Chart](https://www.fusioncharts.com/dev/getting-started/react/your-first-chart-using-react) 61 | - [List Of Charts](https://www.fusioncharts.com/dev/chart-guide/list-of-charts) 62 | - [Themes](https://www.fusioncharts.com/dev/themes/introduction-to-themes) 63 | 64 | ## Auth0 65 | 66 | - [Auth0 - Main Docs](https://auth0.com/) 67 | 68 | - Create Application 69 | - Choose : Single Page Web Applications 70 | - Choose : React 71 | - Go to Settings Tab 72 | - Copy/Paste Domain, ClientID - can be public (or use .env) 73 | - Add Domain - 74 | for now http://localhost:3000 (DON'T COPY PASTE FROM URL BAR) 75 | 76 | - Allowed Callback URLs 77 | - Allowed Logout URLs 78 | - Allowed Web Origins 79 | - SAVE CHANGES!!!!!!!!!!!!!!! 80 | 81 | - Connections 82 | email,social 83 | 84 | - [React SDK Docs](https://auth0.com/docs/libraries/auth0-react) 85 | - [REACT SDK API Docs](https://auth0.github.io/auth0-react/) 86 | 87 | ## Deployment 88 | 89 | [Netlify](https://www.netlify.com/) 90 | 91 | ## Additional Info 92 | 93 | #### Redirects with react-router-dom 94 | 95 | In order for routing to work on netlify, redirects was added to the public folder 96 | 97 | - \_redirects file in public 98 | 99 | ``` 100 | 101 | /* /index.html 200 102 | 103 | ``` 104 | 105 | [Redirects Blog Post](https://dev.to/dance2die/page-not-found-on-netlify-with-react-router-58mc) 106 | 107 | #### Warnings and create-react-app 108 | 109 | package.json 110 | 111 | ```js 112 | "build": "CI= react-scripts build", 113 | ``` 114 | 115 | [create-react-app Warning Fix Blog Post](https://community.netlify.com/t/how-to-fix-build-failures-with-create-react-app-in-production/17752) 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-search", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@auth0/auth0-react": "^1.12.0", 7 | "@testing-library/jest-dom": "^5.16.5", 8 | "@testing-library/react": "^13.4.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "axios": "^1.2.1", 11 | "fusioncharts": "^3.19.0", 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-fusioncharts": "^4.0.0", 15 | "react-icons": "^4.7.1", 16 | "react-router-dom": "^6.4.5", 17 | "react-scripts": "5.0.1", 18 | "styled-components": "^5.3.6", 19 | "web-vitals": "^2.1.4" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "CI= react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": "react-app" 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-search-github-users/bad10334cdf5b057f9da9f3a70cf97dff4f630bb/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Search Github User 28 | 29 | 30 | You need to enable JavaScript to run this app. 31 | 32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-search-github-users/bad10334cdf5b057f9da9f3a70cf97dff4f630bb/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-search-github-users/bad10334cdf5b057f9da9f3a70cf97dff4f630bb/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dashboard, Login, PrivateRoute, AuthWrapper, Error } from './pages'; 3 | import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; 4 | 5 | function App() { 6 | return ( 7 | 8 | 9 | 10 | 14 | 15 | 16 | } 17 | /> 18 | } /> 19 | } /> 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/components/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GithubContext } from '../context/context'; 3 | import styled from 'styled-components'; 4 | import { MdBusiness, MdLocationOn, MdLink } from 'react-icons/md'; 5 | const Card = () => { 6 | const { githubUser } = React.useContext(GithubContext); 7 | const { 8 | avatar_url, 9 | html_url, 10 | name, 11 | company, 12 | blog, 13 | bio, 14 | location, 15 | twitter_username, 16 | } = githubUser; 17 | return ( 18 | 19 | 20 | 21 | 22 | {name} 23 | @{twitter_username || 'john doe'} 24 | 25 | follow 26 | 27 | {bio} 28 | 29 | 30 | {company} 31 | 32 | 33 | {location || 'earth'} 34 | 35 | 36 | 37 | {blog} 38 | 39 | 40 | 41 | ); 42 | }; 43 | const Wrapper = styled.article` 44 | background: var(--clr-white); 45 | padding: 1.5rem 2rem; 46 | border-top-right-radius: var(--radius); 47 | border-bottom-left-radius: var(--radius); 48 | border-bottom-right-radius: var(--radius); 49 | position: relative; 50 | &::before { 51 | content: 'user'; 52 | position: absolute; 53 | top: 0; 54 | left: 0; 55 | transform: translateY(-100%); 56 | background: var(--clr-white); 57 | color: var(--clr-grey-5); 58 | border-top-right-radius: var(--radius); 59 | border-top-left-radius: var(--radius); 60 | text-transform: capitalize; 61 | padding: 0.5rem 1rem 0 1rem; 62 | letter-spacing: var(--spacing); 63 | font-size: 1rem; 64 | } 65 | header { 66 | display: grid; 67 | grid-template-columns: auto 1fr auto; 68 | align-items: center; 69 | column-gap: 1rem; 70 | margin-bottom: 1rem; 71 | img { 72 | width: 75px; 73 | height: 75px; 74 | border-radius: 50%; 75 | } 76 | h4 { 77 | margin-bottom: 0.25rem; 78 | } 79 | p { 80 | margin-bottom: 0; 81 | } 82 | a { 83 | color: var(--clr-primary-5); 84 | border: 1px solid var(--clr-primary-5); 85 | padding: 0.25rem 0.75rem; 86 | border-radius: 1rem; 87 | text-transform: capitalize; 88 | letter-spacing: var(--spacing); 89 | transition: var(--transition); 90 | cursor: pointer; 91 | &:hover { 92 | background: var(--clr-primary-5); 93 | color: var(--clr-white); 94 | } 95 | } 96 | } 97 | .bio { 98 | color: var(--clr-grey-3); 99 | } 100 | .links { 101 | p, 102 | a { 103 | margin-bottom: 0.25rem; 104 | display: flex; 105 | align-items: center; 106 | svg { 107 | margin-right: 0.5rem; 108 | font-size: 1.3rem; 109 | } 110 | } 111 | a { 112 | color: var(--clr-primary-5); 113 | transition: var(--transition); 114 | svg { 115 | color: var(--clr-grey-5); 116 | } 117 | &:hover { 118 | color: var(--clr-primary-3); 119 | } 120 | } 121 | } 122 | `; 123 | export default Card; 124 | -------------------------------------------------------------------------------- /src/components/Charts/Bar.js: -------------------------------------------------------------------------------- 1 | // STEP 1 - Include Dependencies 2 | // Include react 3 | import React from 'react'; 4 | 5 | // Include the react-fusioncharts component 6 | import ReactFC from 'react-fusioncharts'; 7 | 8 | // Include the fusioncharts library 9 | import FusionCharts from 'fusioncharts'; 10 | 11 | // Include the chart type 12 | import Chart from 'fusioncharts/fusioncharts.charts'; 13 | 14 | 15 | // Adding the chart and theme as dependency to the core fusioncharts 16 | ReactFC.fcRoot(FusionCharts, Chart); 17 | 18 | // STEP 3 - Creating the JSON object to store the chart configurations 19 | 20 | const ChartComponent = ({ data }) => { 21 | const chartConfigs = { 22 | type: 'bar2d', // The chart type 23 | width: '100%', // Width of the chart 24 | height: '350', // Height of the chart 25 | dataFormat: 'json', // Data type 26 | dataSource: { 27 | // Chart Configuration 28 | chart: { 29 | caption: 'Most Forked', 30 | yAxisName: 'Forks', 31 | xAxisName: 'Repos', 32 | xAxisNameFontSize: 16, 33 | yAxisNameFontSize: 16, 34 | showCanvasBorder: 0, 35 | showAlternateVGridColor: 0, 36 | usePlotGradientColor: 0, 37 | valueFontSize: 16, 38 | placeValuesInside: 0, 39 | divLineColor: "#102a42", 40 | divLineAlpha: 15, 41 | captionFontColor: "#102a42", 42 | captionFontBold: 0, 43 | captionFontSize: 20, 44 | captionFont: "Roboto", 45 | baseFont: "Open Sans", 46 | baseFontSize: 12, 47 | baseFontColor: "#617d98", 48 | smartLineColor: "#617d98", 49 | showShadow: 0, 50 | showPlotBorder: 0, 51 | paletteColors: 52 | "#2caeba, #5D62B5, #FFC533, #F2726F, #8d6e63, #1de9b6, #6E80CA", 53 | bgColor: "#FFFFFF", 54 | showBorder: 0, 55 | }, 56 | // Chart Data 57 | data, 58 | }, 59 | }; 60 | 61 | return ; 62 | }; 63 | 64 | export default ChartComponent; 65 | -------------------------------------------------------------------------------- /src/components/Charts/Column.js: -------------------------------------------------------------------------------- 1 | // STEP 1 - Include Dependencies 2 | // Include react 3 | import React from 'react'; 4 | 5 | // Include the react-fusioncharts component 6 | import ReactFC from 'react-fusioncharts'; 7 | 8 | // Include the fusioncharts library 9 | import FusionCharts from 'fusioncharts'; 10 | 11 | // Include the chart type 12 | import Chart from 'fusioncharts/fusioncharts.charts'; 13 | 14 | // Adding the chart and theme as dependency to the core fusioncharts 15 | ReactFC.fcRoot(FusionCharts, Chart); 16 | 17 | // STEP 3 - Creating the JSON object to store the chart configurations 18 | 19 | const ChartComponent = ({ data }) => { 20 | const chartConfigs = { 21 | type: 'column2d', // The chart type 22 | width: '100%', // Width of the chart 23 | height: '350', // Height of the chart 24 | dataFormat: 'json', // Data type 25 | dataSource: { 26 | // Chart Configuration 27 | chart: { 28 | caption: 'Most Popular', 29 | yAxisName: 'Stars', 30 | yAxisNameFontSize: 16, 31 | xAxisName: 'Repos', 32 | xAxisNameFontSize: 16, 33 | showCanvasBorder: 0, 34 | showAlternateHGridColor: 0, 35 | usePlotGradientColor: 0, 36 | valueFontSize: 16, 37 | placeValuesInside: 0, 38 | divLineColor: "#102a42", 39 | divLineAlpha: 15, 40 | captionFontColor: "#102a42", 41 | captionFontBold: 0, 42 | captionFontSize: 20, 43 | captionFont: "Roboto", 44 | baseFont: "Open Sans", 45 | baseFontSize: 12, 46 | baseFontColor: "#617d98", 47 | smartLineColor: "#617d98", 48 | showShadow: 0, 49 | showPlotBorder: 0, 50 | paletteColors: 51 | "#2caeba, #5D62B5, #FFC533, #F2726F, #8d6e63, #1de9b6, #6E80CA", 52 | bgColor: "#FFFFFF", 53 | showBorder: 0, 54 | }, 55 | // Chart Data 56 | data, 57 | }, 58 | }; 59 | 60 | return ; 61 | }; 62 | 63 | export default ChartComponent; 64 | -------------------------------------------------------------------------------- /src/components/Charts/Doughnut.js: -------------------------------------------------------------------------------- 1 | // STEP 1 - Include Dependencies 2 | // Include react 3 | import React from "react"; 4 | 5 | // Include the react-fusioncharts component 6 | import ReactFC from "react-fusioncharts"; 7 | 8 | // Include the fusioncharts library 9 | import FusionCharts from "fusioncharts"; 10 | 11 | // Include the chart type 12 | import Chart from "fusioncharts/fusioncharts.charts"; 13 | 14 | // Adding the chart and theme as dependency to the core fusioncharts 15 | ReactFC.fcRoot(FusionCharts, Chart); 16 | 17 | // STEP 3 - Creating the JSON object to store the chart configurations 18 | 19 | const ChartComponent = ({ data }) => { 20 | const chartConfigs = { 21 | type: "doughnut2d", // The chart type 22 | width: "100%", // Width of the chart 23 | height: "350", // Height of the chart 24 | dataFormat: "json", // Data type 25 | dataSource: { 26 | // Chart Configuration 27 | chart: { 28 | caption: "Stars Per Language", 29 | decimals: 0, 30 | pieRadius: "45%", 31 | doughnutRadius: "60%", 32 | showPercentValues: 0, 33 | captionFontColor: "#102a42", 34 | captionFontBold: 0, 35 | captionFontSize: 20, 36 | captionFont: "Roboto", 37 | baseFont: "Open Sans", 38 | baseFontSize: 16, 39 | baseFontColor: "#617d98", 40 | smartLineColor: "#617d98", 41 | showShadow: 0, 42 | showPlotBorder: 0, 43 | paletteColors: 44 | "#2caeba, #5D62B5, #FFC533, #F2726F, #8d6e63, #1de9b6, #6E80CA", 45 | use3DLighting: 0, 46 | useDataPlotColorForLabels: 0, 47 | bgColor: "#FFFFFF", 48 | showBorder: 0, 49 | }, 50 | // Chart Data 51 | data, 52 | }, 53 | }; 54 | 55 | return ; 56 | }; 57 | 58 | export default ChartComponent; 59 | -------------------------------------------------------------------------------- /src/components/Charts/Pie.js: -------------------------------------------------------------------------------- 1 | // STEP 1 - Include Dependencies 2 | // Include react 3 | import React from "react"; 4 | 5 | // Include the react-fusioncharts component 6 | import ReactFC from "react-fusioncharts"; 7 | 8 | // Include the fusioncharts library 9 | import FusionCharts from "fusioncharts"; 10 | 11 | // Include the chart type 12 | import Chart from "fusioncharts/fusioncharts.charts"; 13 | 14 | // Include the theme as fusion 15 | import FusionTheme from "fusioncharts/themes/fusioncharts.theme.fusion"; 16 | 17 | // Adding the chart and theme as dependency to the core fusioncharts 18 | ReactFC.fcRoot(FusionCharts, Chart, FusionTheme); 19 | 20 | // STEP 3 - Creating the JSON object to store the chart configurations 21 | 22 | const ChartComponent = ({ data }) => { 23 | const chartConfigs = { 24 | type: "pie2d", // The chart type 25 | width: "100%", // Width of the chart 26 | height: "350", // Height of the chart 27 | dataFormat: "json", // Data type 28 | dataSource: { 29 | // Chart Configuration 30 | chart: { 31 | caption: "Languages", 32 | captionFontColor: "#102a42", 33 | captionFontBold: 0, 34 | captionFontSize: 20, 35 | captionFont: "Roboto", 36 | baseFont: "Open Sans", 37 | baseFontSize: 16, 38 | baseFontColor: "#617d98", 39 | smartLineColor: "#617d98", 40 | showShadow: 0, 41 | showPlotBorder: 0, 42 | paletteColors: 43 | "#2caeba, #5D62B5, #FFC533, #F2726F, #8d6e63, #1de9b6, #6E80CA", 44 | use3DLighting: 0, 45 | useDataPlotColorForLabels: 0, 46 | bgColor: "#FFFFFF", 47 | showBorder: 0, 48 | decimals: 0, 49 | pieRadius: "45%", 50 | }, 51 | // Chart Data 52 | data, 53 | }, 54 | }; 55 | 56 | return ; 57 | }; 58 | 59 | export default ChartComponent; 60 | -------------------------------------------------------------------------------- /src/components/Charts/index.js: -------------------------------------------------------------------------------- 1 | import Column from "./Column"; 2 | import Bar from "./Bar"; 3 | import Pie from "./Pie"; 4 | import Doughnut from "./Doughnut"; 5 | 6 | export { Pie, Column, Bar, Doughnut }; 7 | -------------------------------------------------------------------------------- /src/components/Followers.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GithubContext } from '../context/context'; 3 | import styled from 'styled-components'; 4 | 5 | const Followers = () => { 6 | const { followers } = React.useContext(GithubContext); 7 | 8 | return ( 9 | 10 | 11 | {followers.map((follower, index) => { 12 | const { avatar_url: img, html_url, login } = follower; 13 | return ( 14 | 15 | 16 | 17 | {login} 18 | {html_url} 19 | 20 | 21 | ); 22 | })} 23 | 24 | 25 | ); 26 | }; 27 | 28 | const Wrapper = styled.article` 29 | background: var(--clr-white); 30 | border-top-right-radius: var(--radius); 31 | border-bottom-left-radius: var(--radius); 32 | border-bottom-right-radius: var(--radius); 33 | position: relative; 34 | 35 | &::before { 36 | content: ' followers'; 37 | position: absolute; 38 | top: 0; 39 | left: 0; 40 | transform: translateY(-100%); 41 | background: var(--clr-white); 42 | color: var(--clr-grey-5); 43 | border-top-right-radius: var(--radius); 44 | border-top-left-radius: var(--radius); 45 | text-transform: capitalize; 46 | padding: 0.5rem 1rem 0 1rem; 47 | letter-spacing: var(--spacing); 48 | font-size: 1rem; 49 | } 50 | .followers { 51 | overflow: scroll; 52 | height: 260px; 53 | display: grid; 54 | grid-template-rows: repeat(auto-fill, minmax(45px, 1fr)); 55 | gap: 1.25rem 1rem; 56 | padding: 1rem 2rem; 57 | } 58 | article { 59 | transition: var(--transition); 60 | padding: 0.15rem 0.5rem; 61 | border-radius: var(--radius); 62 | display: grid; 63 | grid-template-columns: auto 1fr; 64 | align-items: center; 65 | column-gap: 1rem; 66 | img { 67 | height: 100%; 68 | width: 45px; 69 | border-radius: 50%; 70 | object-fit: cover; 71 | } 72 | h4 { 73 | margin-bottom: 0; 74 | } 75 | a { 76 | color: var(--clr-grey-5); 77 | } 78 | } 79 | `; 80 | export default Followers; 81 | -------------------------------------------------------------------------------- /src/components/Info.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GithubContext } from '../context/context'; 3 | import styled from 'styled-components'; 4 | import { GoRepo, GoGist } from 'react-icons/go'; 5 | import { FiUsers, FiUserPlus } from 'react-icons/fi'; 6 | 7 | const UserInfo = () => { 8 | const { githubUser } = React.useContext(GithubContext); 9 | const { public_repos, followers, following, public_gists } = githubUser; 10 | 11 | const items = [ 12 | { 13 | id: 1, 14 | icon: , 15 | label: 'repos', 16 | value: public_repos, 17 | color: 'pink', 18 | }, 19 | { 20 | id: 2, 21 | icon: , 22 | label: 'followers', 23 | value: followers, 24 | color: 'green', 25 | }, 26 | { 27 | id: 3, 28 | icon: , 29 | label: 'following', 30 | value: following, 31 | color: 'purple', 32 | }, 33 | { 34 | id: 4, 35 | icon: , 36 | label: 'gists', 37 | value: public_gists, 38 | color: 'yellow', 39 | }, 40 | ]; 41 | 42 | return ( 43 | 44 | 45 | {items.map((item) => { 46 | return ; 47 | })} 48 | 49 | 50 | ); 51 | }; 52 | 53 | const Item = ({ icon, label, value, color }) => { 54 | return ( 55 | 56 | {icon} 57 | 58 | {value} 59 | {label} 60 | 61 | 62 | ); 63 | }; 64 | 65 | const Wrapper = styled.section` 66 | display: grid; 67 | grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 68 | gap: 1rem 2rem; 69 | @media (min-width: 640px) { 70 | grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); 71 | } 72 | .item { 73 | border-radius: var(--radius); 74 | padding: 1rem 2rem; 75 | background: var(--clr-white); 76 | display: grid; 77 | grid-template-columns: auto 1fr; 78 | column-gap: 3rem; 79 | align-items: center; 80 | span { 81 | width: 3rem; 82 | height: 3rem; 83 | display: grid; 84 | place-items: center; 85 | border-radius: 50%; 86 | } 87 | .icon { 88 | font-size: 1.5rem; 89 | } 90 | h3 { 91 | margin-bottom: 0; 92 | letter-spacing: 0; 93 | } 94 | p { 95 | margin-bottom: 0; 96 | text-transform: capitalize; 97 | } 98 | .pink { 99 | background: #ffe0f0; 100 | color: #da4a91; 101 | } 102 | .green { 103 | background: var(--clr-primary-10); 104 | color: var(--clr-primary-5); 105 | } 106 | .purple { 107 | background: #e6e6ff; 108 | color: #5d55fa; 109 | } 110 | .yellow { 111 | background: #fffbea; 112 | color: #f0b429; 113 | } 114 | } 115 | `; 116 | 117 | export default UserInfo; 118 | -------------------------------------------------------------------------------- /src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { useAuth0 } from '@auth0/auth0-react'; 4 | 5 | const Navbar = () => { 6 | const { 7 | isAuthenticated, 8 | loginWithRedirect, 9 | logout, 10 | user, 11 | isLoading, 12 | } = useAuth0(); 13 | const isUser = isAuthenticated && user; 14 | 15 | return ( 16 | 17 | {isUser && user.picture && } 18 | {isUser && user.name && ( 19 | 20 | Welcome, {user.name.toUpperCase()} 21 | 22 | )} 23 | {isUser ? ( 24 | { 26 | logout({ returnTo: window.location.origin }); 27 | }} 28 | > 29 | logout 30 | 31 | ) : ( 32 | login 33 | )} 34 | 35 | ); 36 | }; 37 | 38 | const Wrapper = styled.nav` 39 | padding: 1.5rem; 40 | margin-bottom: 4rem; 41 | background: var(--clr-white); 42 | text-align: center; 43 | display: grid; 44 | grid-template-columns: auto auto 100px; 45 | justify-content: center; 46 | align-items: center; 47 | gap: 1.5rem; 48 | h4 { 49 | margin-bottom: 0; 50 | font-weight: 400; 51 | } 52 | img { 53 | width: 35px !important; 54 | height: 35px; 55 | border-radius: 50%; 56 | object-fit: cover; 57 | } 58 | button { 59 | background: transparent; 60 | border: transparent; 61 | font-size: 1.2rem; 62 | text-transform: capitalize; 63 | letter-spacing: var(--spacing); 64 | color: var(--clr-grey-5); 65 | cursor: pointer; 66 | } 67 | `; 68 | 69 | export default Navbar; 70 | -------------------------------------------------------------------------------- /src/components/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { GithubContext } from '../context/context'; 4 | import { Pie, Column, Bar, Doughnut } from './Charts'; 5 | const Repos = () => { 6 | const { repos } = React.useContext(GithubContext); 7 | 8 | const languages = repos.reduce((total, item) => { 9 | const { language, stargazers_count } = item; 10 | if (!language) return total; 11 | if (!total[language]) { 12 | total[language] = { label: language, value: 1, stars: stargazers_count }; 13 | } else { 14 | total[language] = { 15 | ...total[language], 16 | value: total[language].value + 1, 17 | stars: total[language].stars + stargazers_count, 18 | }; 19 | } 20 | return total; 21 | }, {}); 22 | 23 | const mostUsed = Object.values(languages) 24 | .sort((a, b) => { 25 | return b.value - a.value; 26 | }) 27 | .slice(0, 5); 28 | 29 | // most stars per language 30 | 31 | const mostPopular = Object.values(languages) 32 | .sort((a, b) => { 33 | return b.stars - a.stars; 34 | }) 35 | .map((item) => { 36 | return { ...item, value: item.stars }; 37 | }) 38 | .slice(0, 5); 39 | 40 | // stars, forks 41 | 42 | let { stars, forks } = repos.reduce( 43 | (total, item) => { 44 | const { stargazers_count, name, forks } = item; 45 | total.stars[stargazers_count] = { label: name, value: stargazers_count }; 46 | total.forks[forks] = { label: name, value: forks }; 47 | return total; 48 | }, 49 | { 50 | stars: {}, 51 | forks: {}, 52 | } 53 | ); 54 | 55 | stars = Object.values(stars).slice(-5).reverse(); 56 | forks = Object.values(forks).slice(-5).reverse(); 57 | 58 | return ( 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | ); 68 | }; 69 | 70 | const Wrapper = styled.div` 71 | display: grid; 72 | justify-items: center; 73 | gap: 2rem; 74 | @media (min-width: 800px) { 75 | grid-template-columns: 1fr 1fr; 76 | } 77 | 78 | @media (min-width: 1200px) { 79 | grid-template-columns: 2fr 3fr; 80 | } 81 | 82 | div { 83 | width: 100% !important; 84 | } 85 | .fusioncharts-container { 86 | width: 100% !important; 87 | } 88 | svg { 89 | width: 100% !important; 90 | border-radius: var(--radius) !important; 91 | } 92 | `; 93 | 94 | export default Repos; 95 | -------------------------------------------------------------------------------- /src/components/Search.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { MdSearch } from 'react-icons/md'; 4 | import { GithubContext } from '../context/context'; 5 | const Search = () => { 6 | const [user, setUser] = React.useState(''); 7 | const { requests, error, searchGithubUser, isLoading } = React.useContext( 8 | GithubContext 9 | ); 10 | // get things from global context 11 | const handleSubmit = (e) => { 12 | e.preventDefault(); 13 | if (user) { 14 | // more logic coming up soon 15 | searchGithubUser(user); 16 | //optional 17 | // setUser(''); 18 | } 19 | }; 20 | return ( 21 | 22 | 23 | {error.show && ( 24 | 25 | {error.msg} 26 | 27 | )} 28 | 29 | 30 | 31 | setUser(e.target.value)} 36 | /> 37 | {requests > 0 && !isLoading && ( 38 | search 39 | )} 40 | 41 | 42 | requests : {requests} / 60 43 | 44 | 45 | ); 46 | }; 47 | 48 | const Wrapper = styled.div` 49 | position: relative; 50 | display: grid; 51 | gap: 1rem 1.75rem; 52 | @media (min-width: 768px) { 53 | grid-template-columns: 1fr max-content; 54 | align-items: center; 55 | h3 { 56 | padding: 0 0.5rem; 57 | } 58 | } 59 | .form-control { 60 | background: var(--clr-white); 61 | display: grid; 62 | align-items: center; 63 | grid-template-columns: auto 1fr auto; 64 | column-gap: 0.5rem; 65 | border-radius: 5px; 66 | padding: 0.5rem; 67 | input { 68 | border-color: transparent; 69 | outline-color: var(--clr-grey-10); 70 | letter-spacing: var(--spacing); 71 | color: var(--clr-grey-3); 72 | padding: 0.25rem 0.5rem; 73 | } 74 | input::placeholder { 75 | color: var(--clr-grey-3); 76 | text-transform: capitalize; 77 | letter-spacing: var(--spacing); 78 | } 79 | button { 80 | border-radius: 5px; 81 | border-color: transparent; 82 | padding: 0.25rem 0.5rem; 83 | text-transform: capitalize; 84 | letter-spacing: var(--spacing); 85 | background: var(--clr-primary-5); 86 | color: var(--clr-white); 87 | transition: var(--transition); 88 | cursor: pointer; 89 | &:hover { 90 | background: var(--clr-primary-8); 91 | color: var(--clr-primary-1); 92 | } 93 | } 94 | 95 | svg { 96 | color: var(--clr-grey-5); 97 | } 98 | input, 99 | button, 100 | svg { 101 | font-size: 1.3rem; 102 | } 103 | @media (max-width: 800px) { 104 | button, 105 | input, 106 | svg { 107 | font-size: 0.85rem; 108 | } 109 | } 110 | } 111 | h3 { 112 | margin-bottom: 0; 113 | color: var(--clr-grey-5); 114 | font-weight: 400; 115 | } 116 | `; 117 | const ErrorWrapper = styled.article` 118 | position: absolute; 119 | width: 90vw; 120 | top: 0; 121 | left: 0; 122 | transform: translateY(-100%); 123 | text-transform: capitalize; 124 | p { 125 | color: red; 126 | letter-spacing: var(--spacing); 127 | } 128 | `; 129 | export default Search; 130 | -------------------------------------------------------------------------------- /src/components/User.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Card from './Card'; 4 | import Followers from './Followers'; 5 | const User = () => { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | }; 15 | 16 | const Wrapper = styled.div` 17 | padding-top: 2rem; 18 | display: grid; 19 | gap: 3rem 2rem; 20 | @media (min-width: 992px) { 21 | grid-template-columns: 1fr 1fr; 22 | } 23 | /* align-items: start; */ 24 | `; 25 | 26 | export default User; 27 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import Info from "./Info"; 2 | import Repos from "./Repos"; 3 | import User from "./User"; 4 | import Search from "./Search"; 5 | import Navbar from "./Navbar"; 6 | 7 | export { Info, Repos, User, Search, Navbar }; 8 | -------------------------------------------------------------------------------- /src/context/context.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import mockUser from './mockData.js/mockUser'; 3 | import mockRepos from './mockData.js/mockRepos'; 4 | import mockFollowers from './mockData.js/mockFollowers'; 5 | import axios from 'axios'; 6 | 7 | const rootUrl = 'https://api.github.com'; 8 | 9 | const GithubContext = React.createContext(); 10 | 11 | // Provider, Consumer - GithubContext.Provider 12 | 13 | const GithubProvider = ({ children }) => { 14 | const [githubUser, setGithubUser] = useState(mockUser); 15 | const [repos, setRepos] = useState(mockRepos); 16 | const [followers, setFollowers] = useState(mockFollowers); 17 | // request loading 18 | const [requests, setRequests] = useState(0); 19 | const [isLoading, setIsLoading] = useState(false); 20 | // error 21 | const [error, setError] = useState({ show: false, msg: '' }); 22 | 23 | const searchGithubUser = async (user) => { 24 | toggleError(); 25 | setIsLoading(true); 26 | const response = await axios(`${rootUrl}/users/${user}`).catch((err) => 27 | console.log(err) 28 | ); 29 | if (response) { 30 | setGithubUser(response.data); 31 | const { login, followers_url } = response.data; 32 | 33 | await Promise.allSettled([ 34 | axios(`${rootUrl}/users/${login}/repos?per_page=100`), 35 | axios(`${followers_url}?per_page=100`), 36 | ]) 37 | .then((results) => { 38 | const [repos, followers] = results; 39 | const status = 'fulfilled'; 40 | if (repos.status === status) { 41 | setRepos(repos.value.data); 42 | } 43 | if (followers.status === status) { 44 | setFollowers(followers.value.data); 45 | } 46 | }) 47 | .catch((err) => console.log(err)); 48 | } else { 49 | toggleError(true, 'there is no user with that username'); 50 | } 51 | checkRequests(); 52 | setIsLoading(false); 53 | }; 54 | 55 | // check rate 56 | const checkRequests = () => { 57 | axios(`${rootUrl}/rate_limit`) 58 | .then(({ data }) => { 59 | let { 60 | rate: { remaining }, 61 | } = data; 62 | setRequests(remaining); 63 | if (remaining === 0) { 64 | toggleError(true, 'sorry, you have exceeded your hourly rate limit!'); 65 | } 66 | }) 67 | .catch((err) => console.log(err)); 68 | }; 69 | function toggleError(show = false, msg = '') { 70 | setError({ show, msg }); 71 | } 72 | // error 73 | useEffect(checkRequests, []); 74 | // get initial user 75 | useEffect(() => { 76 | searchGithubUser('john-smilga'); 77 | }, []); 78 | return ( 79 | 90 | {children} 91 | 92 | ); 93 | }; 94 | 95 | export { GithubProvider, GithubContext }; 96 | -------------------------------------------------------------------------------- /src/context/mockData.js/mockFollowers.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | login: 'ThaELL1', 4 | id: 37319124, 5 | node_id: 'MDQ6VXNlcjM3MzE5MTI0', 6 | avatar_url: 'https://avatars3.githubusercontent.com/u/37319124?v=4', 7 | gravatar_id: '', 8 | url: 'https://api.github.com/users/ThaELL1', 9 | html_url: 'https://github.com/ThaELL1', 10 | followers_url: 'https://api.github.com/users/ThaELL1/followers', 11 | following_url: 12 | 'https://api.github.com/users/ThaELL1/following{/other_user}', 13 | gists_url: 'https://api.github.com/users/ThaELL1/gists{/gist_id}', 14 | starred_url: 'https://api.github.com/users/ThaELL1/starred{/owner}{/repo}', 15 | subscriptions_url: 'https://api.github.com/users/ThaELL1/subscriptions', 16 | organizations_url: 'https://api.github.com/users/ThaELL1/orgs', 17 | repos_url: 'https://api.github.com/users/ThaELL1/repos', 18 | events_url: 'https://api.github.com/users/ThaELL1/events{/privacy}', 19 | received_events_url: 'https://api.github.com/users/ThaELL1/received_events', 20 | type: 'User', 21 | site_admin: false, 22 | }, 23 | { 24 | login: 'degreeck', 25 | id: 31404082, 26 | node_id: 'MDQ6VXNlcjMxNDA0MDgy', 27 | avatar_url: 'https://avatars1.githubusercontent.com/u/31404082?v=4', 28 | gravatar_id: '', 29 | url: 'https://api.github.com/users/degreeck', 30 | html_url: 'https://github.com/degreeck', 31 | followers_url: 'https://api.github.com/users/degreeck/followers', 32 | following_url: 33 | 'https://api.github.com/users/degreeck/following{/other_user}', 34 | gists_url: 'https://api.github.com/users/degreeck/gists{/gist_id}', 35 | starred_url: 'https://api.github.com/users/degreeck/starred{/owner}{/repo}', 36 | subscriptions_url: 'https://api.github.com/users/degreeck/subscriptions', 37 | organizations_url: 'https://api.github.com/users/degreeck/orgs', 38 | repos_url: 'https://api.github.com/users/degreeck/repos', 39 | events_url: 'https://api.github.com/users/degreeck/events{/privacy}', 40 | received_events_url: 41 | 'https://api.github.com/users/degreeck/received_events', 42 | type: 'User', 43 | site_admin: false, 44 | }, 45 | { 46 | login: 'Kemmy91', 47 | id: 42711367, 48 | node_id: 'MDQ6VXNlcjQyNzExMzY3', 49 | avatar_url: 'https://avatars2.githubusercontent.com/u/42711367?v=4', 50 | gravatar_id: '', 51 | url: 'https://api.github.com/users/Kemmy91', 52 | html_url: 'https://github.com/Kemmy91', 53 | followers_url: 'https://api.github.com/users/Kemmy91/followers', 54 | following_url: 55 | 'https://api.github.com/users/Kemmy91/following{/other_user}', 56 | gists_url: 'https://api.github.com/users/Kemmy91/gists{/gist_id}', 57 | starred_url: 'https://api.github.com/users/Kemmy91/starred{/owner}{/repo}', 58 | subscriptions_url: 'https://api.github.com/users/Kemmy91/subscriptions', 59 | organizations_url: 'https://api.github.com/users/Kemmy91/orgs', 60 | repos_url: 'https://api.github.com/users/Kemmy91/repos', 61 | events_url: 'https://api.github.com/users/Kemmy91/events{/privacy}', 62 | received_events_url: 'https://api.github.com/users/Kemmy91/received_events', 63 | type: 'User', 64 | site_admin: false, 65 | }, 66 | { 67 | login: 'alivanoy', 68 | id: 24864699, 69 | node_id: 'MDQ6VXNlcjI0ODY0Njk5', 70 | avatar_url: 'https://avatars2.githubusercontent.com/u/24864699?v=4', 71 | gravatar_id: '', 72 | url: 'https://api.github.com/users/alivanoy', 73 | html_url: 'https://github.com/alivanoy', 74 | followers_url: 'https://api.github.com/users/alivanoy/followers', 75 | following_url: 76 | 'https://api.github.com/users/alivanoy/following{/other_user}', 77 | gists_url: 'https://api.github.com/users/alivanoy/gists{/gist_id}', 78 | starred_url: 'https://api.github.com/users/alivanoy/starred{/owner}{/repo}', 79 | subscriptions_url: 'https://api.github.com/users/alivanoy/subscriptions', 80 | organizations_url: 'https://api.github.com/users/alivanoy/orgs', 81 | repos_url: 'https://api.github.com/users/alivanoy/repos', 82 | events_url: 'https://api.github.com/users/alivanoy/events{/privacy}', 83 | received_events_url: 84 | 'https://api.github.com/users/alivanoy/received_events', 85 | type: 'User', 86 | site_admin: false, 87 | }, 88 | { 89 | login: 'Turanwest', 90 | id: 40670951, 91 | node_id: 'MDQ6VXNlcjQwNjcwOTUx', 92 | avatar_url: 'https://avatars1.githubusercontent.com/u/40670951?v=4', 93 | gravatar_id: '', 94 | url: 'https://api.github.com/users/Turanwest', 95 | html_url: 'https://github.com/Turanwest', 96 | followers_url: 'https://api.github.com/users/Turanwest/followers', 97 | following_url: 98 | 'https://api.github.com/users/Turanwest/following{/other_user}', 99 | gists_url: 'https://api.github.com/users/Turanwest/gists{/gist_id}', 100 | starred_url: 101 | 'https://api.github.com/users/Turanwest/starred{/owner}{/repo}', 102 | subscriptions_url: 'https://api.github.com/users/Turanwest/subscriptions', 103 | organizations_url: 'https://api.github.com/users/Turanwest/orgs', 104 | repos_url: 'https://api.github.com/users/Turanwest/repos', 105 | events_url: 'https://api.github.com/users/Turanwest/events{/privacy}', 106 | received_events_url: 107 | 'https://api.github.com/users/Turanwest/received_events', 108 | type: 'User', 109 | site_admin: false, 110 | }, 111 | { 112 | login: 'cvsiraj95', 113 | id: 20457752, 114 | node_id: 'MDQ6VXNlcjIwNDU3NzUy', 115 | avatar_url: 'https://avatars2.githubusercontent.com/u/20457752?v=4', 116 | gravatar_id: '', 117 | url: 'https://api.github.com/users/cvsiraj95', 118 | html_url: 'https://github.com/cvsiraj95', 119 | followers_url: 'https://api.github.com/users/cvsiraj95/followers', 120 | following_url: 121 | 'https://api.github.com/users/cvsiraj95/following{/other_user}', 122 | gists_url: 'https://api.github.com/users/cvsiraj95/gists{/gist_id}', 123 | starred_url: 124 | 'https://api.github.com/users/cvsiraj95/starred{/owner}{/repo}', 125 | subscriptions_url: 'https://api.github.com/users/cvsiraj95/subscriptions', 126 | organizations_url: 'https://api.github.com/users/cvsiraj95/orgs', 127 | repos_url: 'https://api.github.com/users/cvsiraj95/repos', 128 | events_url: 'https://api.github.com/users/cvsiraj95/events{/privacy}', 129 | received_events_url: 130 | 'https://api.github.com/users/cvsiraj95/received_events', 131 | type: 'User', 132 | site_admin: false, 133 | }, 134 | { 135 | login: 'sidharthchawda', 136 | id: 33715740, 137 | node_id: 'MDQ6VXNlcjMzNzE1NzQw', 138 | avatar_url: 'https://avatars3.githubusercontent.com/u/33715740?v=4', 139 | gravatar_id: '', 140 | url: 'https://api.github.com/users/sidharthchawda', 141 | html_url: 'https://github.com/sidharthchawda', 142 | followers_url: 'https://api.github.com/users/sidharthchawda/followers', 143 | following_url: 144 | 'https://api.github.com/users/sidharthchawda/following{/other_user}', 145 | gists_url: 'https://api.github.com/users/sidharthchawda/gists{/gist_id}', 146 | starred_url: 147 | 'https://api.github.com/users/sidharthchawda/starred{/owner}{/repo}', 148 | subscriptions_url: 149 | 'https://api.github.com/users/sidharthchawda/subscriptions', 150 | organizations_url: 'https://api.github.com/users/sidharthchawda/orgs', 151 | repos_url: 'https://api.github.com/users/sidharthchawda/repos', 152 | events_url: 'https://api.github.com/users/sidharthchawda/events{/privacy}', 153 | received_events_url: 154 | 'https://api.github.com/users/sidharthchawda/received_events', 155 | type: 'User', 156 | site_admin: false, 157 | }, 158 | { 159 | login: 'mahmudalhakim', 160 | id: 16646383, 161 | node_id: 'MDQ6VXNlcjE2NjQ2Mzgz', 162 | avatar_url: 'https://avatars0.githubusercontent.com/u/16646383?v=4', 163 | gravatar_id: '', 164 | url: 'https://api.github.com/users/mahmudalhakim', 165 | html_url: 'https://github.com/mahmudalhakim', 166 | followers_url: 'https://api.github.com/users/mahmudalhakim/followers', 167 | following_url: 168 | 'https://api.github.com/users/mahmudalhakim/following{/other_user}', 169 | gists_url: 'https://api.github.com/users/mahmudalhakim/gists{/gist_id}', 170 | starred_url: 171 | 'https://api.github.com/users/mahmudalhakim/starred{/owner}{/repo}', 172 | subscriptions_url: 173 | 'https://api.github.com/users/mahmudalhakim/subscriptions', 174 | organizations_url: 'https://api.github.com/users/mahmudalhakim/orgs', 175 | repos_url: 'https://api.github.com/users/mahmudalhakim/repos', 176 | events_url: 'https://api.github.com/users/mahmudalhakim/events{/privacy}', 177 | received_events_url: 178 | 'https://api.github.com/users/mahmudalhakim/received_events', 179 | type: 'User', 180 | site_admin: false, 181 | }, 182 | { 183 | login: 'messanjay31', 184 | id: 41499303, 185 | node_id: 'MDQ6VXNlcjQxNDk5MzAz', 186 | avatar_url: 'https://avatars0.githubusercontent.com/u/41499303?v=4', 187 | gravatar_id: '', 188 | url: 'https://api.github.com/users/messanjay31', 189 | html_url: 'https://github.com/messanjay31', 190 | followers_url: 'https://api.github.com/users/messanjay31/followers', 191 | following_url: 192 | 'https://api.github.com/users/messanjay31/following{/other_user}', 193 | gists_url: 'https://api.github.com/users/messanjay31/gists{/gist_id}', 194 | starred_url: 195 | 'https://api.github.com/users/messanjay31/starred{/owner}{/repo}', 196 | subscriptions_url: 'https://api.github.com/users/messanjay31/subscriptions', 197 | organizations_url: 'https://api.github.com/users/messanjay31/orgs', 198 | repos_url: 'https://api.github.com/users/messanjay31/repos', 199 | events_url: 'https://api.github.com/users/messanjay31/events{/privacy}', 200 | received_events_url: 201 | 'https://api.github.com/users/messanjay31/received_events', 202 | type: 'User', 203 | site_admin: false, 204 | }, 205 | { 206 | login: 'jha900', 207 | id: 5097792, 208 | node_id: 'MDQ6VXNlcjUwOTc3OTI=', 209 | avatar_url: 'https://avatars1.githubusercontent.com/u/5097792?v=4', 210 | gravatar_id: '', 211 | url: 'https://api.github.com/users/jha900', 212 | html_url: 'https://github.com/jha900', 213 | followers_url: 'https://api.github.com/users/jha900/followers', 214 | following_url: 'https://api.github.com/users/jha900/following{/other_user}', 215 | gists_url: 'https://api.github.com/users/jha900/gists{/gist_id}', 216 | starred_url: 'https://api.github.com/users/jha900/starred{/owner}{/repo}', 217 | subscriptions_url: 'https://api.github.com/users/jha900/subscriptions', 218 | organizations_url: 'https://api.github.com/users/jha900/orgs', 219 | repos_url: 'https://api.github.com/users/jha900/repos', 220 | events_url: 'https://api.github.com/users/jha900/events{/privacy}', 221 | received_events_url: 'https://api.github.com/users/jha900/received_events', 222 | type: 'User', 223 | site_admin: false, 224 | }, 225 | { 226 | login: 'mechack200', 227 | id: 39648010, 228 | node_id: 'MDQ6VXNlcjM5NjQ4MDEw', 229 | avatar_url: 'https://avatars2.githubusercontent.com/u/39648010?v=4', 230 | gravatar_id: '', 231 | url: 'https://api.github.com/users/mechack200', 232 | html_url: 'https://github.com/mechack200', 233 | followers_url: 'https://api.github.com/users/mechack200/followers', 234 | following_url: 235 | 'https://api.github.com/users/mechack200/following{/other_user}', 236 | gists_url: 'https://api.github.com/users/mechack200/gists{/gist_id}', 237 | starred_url: 238 | 'https://api.github.com/users/mechack200/starred{/owner}{/repo}', 239 | subscriptions_url: 'https://api.github.com/users/mechack200/subscriptions', 240 | organizations_url: 'https://api.github.com/users/mechack200/orgs', 241 | repos_url: 'https://api.github.com/users/mechack200/repos', 242 | events_url: 'https://api.github.com/users/mechack200/events{/privacy}', 243 | received_events_url: 244 | 'https://api.github.com/users/mechack200/received_events', 245 | type: 'User', 246 | site_admin: false, 247 | }, 248 | { 249 | login: 'oromesuo1', 250 | id: 28497078, 251 | node_id: 'MDQ6VXNlcjI4NDk3MDc4', 252 | avatar_url: 'https://avatars3.githubusercontent.com/u/28497078?v=4', 253 | gravatar_id: '', 254 | url: 'https://api.github.com/users/oromesuo1', 255 | html_url: 'https://github.com/oromesuo1', 256 | followers_url: 'https://api.github.com/users/oromesuo1/followers', 257 | following_url: 258 | 'https://api.github.com/users/oromesuo1/following{/other_user}', 259 | gists_url: 'https://api.github.com/users/oromesuo1/gists{/gist_id}', 260 | starred_url: 261 | 'https://api.github.com/users/oromesuo1/starred{/owner}{/repo}', 262 | subscriptions_url: 'https://api.github.com/users/oromesuo1/subscriptions', 263 | organizations_url: 'https://api.github.com/users/oromesuo1/orgs', 264 | repos_url: 'https://api.github.com/users/oromesuo1/repos', 265 | events_url: 'https://api.github.com/users/oromesuo1/events{/privacy}', 266 | received_events_url: 267 | 'https://api.github.com/users/oromesuo1/received_events', 268 | type: 'User', 269 | site_admin: false, 270 | }, 271 | { 272 | login: 'TaiwoOluwasegun', 273 | id: 43286815, 274 | node_id: 'MDQ6VXNlcjQzMjg2ODE1', 275 | avatar_url: 'https://avatars2.githubusercontent.com/u/43286815?v=4', 276 | gravatar_id: '', 277 | url: 'https://api.github.com/users/TaiwoOluwasegun', 278 | html_url: 'https://github.com/TaiwoOluwasegun', 279 | followers_url: 'https://api.github.com/users/TaiwoOluwasegun/followers', 280 | following_url: 281 | 'https://api.github.com/users/TaiwoOluwasegun/following{/other_user}', 282 | gists_url: 'https://api.github.com/users/TaiwoOluwasegun/gists{/gist_id}', 283 | starred_url: 284 | 'https://api.github.com/users/TaiwoOluwasegun/starred{/owner}{/repo}', 285 | subscriptions_url: 286 | 'https://api.github.com/users/TaiwoOluwasegun/subscriptions', 287 | organizations_url: 'https://api.github.com/users/TaiwoOluwasegun/orgs', 288 | repos_url: 'https://api.github.com/users/TaiwoOluwasegun/repos', 289 | events_url: 'https://api.github.com/users/TaiwoOluwasegun/events{/privacy}', 290 | received_events_url: 291 | 'https://api.github.com/users/TaiwoOluwasegun/received_events', 292 | type: 'User', 293 | site_admin: false, 294 | }, 295 | { 296 | login: 'afourteia', 297 | id: 12877426, 298 | node_id: 'MDQ6VXNlcjEyODc3NDI2', 299 | avatar_url: 'https://avatars2.githubusercontent.com/u/12877426?v=4', 300 | gravatar_id: '', 301 | url: 'https://api.github.com/users/afourteia', 302 | html_url: 'https://github.com/afourteia', 303 | followers_url: 'https://api.github.com/users/afourteia/followers', 304 | following_url: 305 | 'https://api.github.com/users/afourteia/following{/other_user}', 306 | gists_url: 'https://api.github.com/users/afourteia/gists{/gist_id}', 307 | starred_url: 308 | 'https://api.github.com/users/afourteia/starred{/owner}{/repo}', 309 | subscriptions_url: 'https://api.github.com/users/afourteia/subscriptions', 310 | organizations_url: 'https://api.github.com/users/afourteia/orgs', 311 | repos_url: 'https://api.github.com/users/afourteia/repos', 312 | events_url: 'https://api.github.com/users/afourteia/events{/privacy}', 313 | received_events_url: 314 | 'https://api.github.com/users/afourteia/received_events', 315 | type: 'User', 316 | site_admin: false, 317 | }, 318 | { 319 | login: 'kozen0', 320 | id: 32563910, 321 | node_id: 'MDQ6VXNlcjMyNTYzOTEw', 322 | avatar_url: 'https://avatars2.githubusercontent.com/u/32563910?v=4', 323 | gravatar_id: '', 324 | url: 'https://api.github.com/users/kozen0', 325 | html_url: 'https://github.com/kozen0', 326 | followers_url: 'https://api.github.com/users/kozen0/followers', 327 | following_url: 'https://api.github.com/users/kozen0/following{/other_user}', 328 | gists_url: 'https://api.github.com/users/kozen0/gists{/gist_id}', 329 | starred_url: 'https://api.github.com/users/kozen0/starred{/owner}{/repo}', 330 | subscriptions_url: 'https://api.github.com/users/kozen0/subscriptions', 331 | organizations_url: 'https://api.github.com/users/kozen0/orgs', 332 | repos_url: 'https://api.github.com/users/kozen0/repos', 333 | events_url: 'https://api.github.com/users/kozen0/events{/privacy}', 334 | received_events_url: 'https://api.github.com/users/kozen0/received_events', 335 | type: 'User', 336 | site_admin: false, 337 | }, 338 | { 339 | login: 'vipinkrishna', 340 | id: 8280880, 341 | node_id: 'MDQ6VXNlcjgyODA4ODA=', 342 | avatar_url: 'https://avatars1.githubusercontent.com/u/8280880?v=4', 343 | gravatar_id: '', 344 | url: 'https://api.github.com/users/vipinkrishna', 345 | html_url: 'https://github.com/vipinkrishna', 346 | followers_url: 'https://api.github.com/users/vipinkrishna/followers', 347 | following_url: 348 | 'https://api.github.com/users/vipinkrishna/following{/other_user}', 349 | gists_url: 'https://api.github.com/users/vipinkrishna/gists{/gist_id}', 350 | starred_url: 351 | 'https://api.github.com/users/vipinkrishna/starred{/owner}{/repo}', 352 | subscriptions_url: 353 | 'https://api.github.com/users/vipinkrishna/subscriptions', 354 | organizations_url: 'https://api.github.com/users/vipinkrishna/orgs', 355 | repos_url: 'https://api.github.com/users/vipinkrishna/repos', 356 | events_url: 'https://api.github.com/users/vipinkrishna/events{/privacy}', 357 | received_events_url: 358 | 'https://api.github.com/users/vipinkrishna/received_events', 359 | type: 'User', 360 | site_admin: false, 361 | }, 362 | { 363 | login: 'ChrisLidyard', 364 | id: 39034474, 365 | node_id: 'MDQ6VXNlcjM5MDM0NDc0', 366 | avatar_url: 'https://avatars1.githubusercontent.com/u/39034474?v=4', 367 | gravatar_id: '', 368 | url: 'https://api.github.com/users/ChrisLidyard', 369 | html_url: 'https://github.com/ChrisLidyard', 370 | followers_url: 'https://api.github.com/users/ChrisLidyard/followers', 371 | following_url: 372 | 'https://api.github.com/users/ChrisLidyard/following{/other_user}', 373 | gists_url: 'https://api.github.com/users/ChrisLidyard/gists{/gist_id}', 374 | starred_url: 375 | 'https://api.github.com/users/ChrisLidyard/starred{/owner}{/repo}', 376 | subscriptions_url: 377 | 'https://api.github.com/users/ChrisLidyard/subscriptions', 378 | organizations_url: 'https://api.github.com/users/ChrisLidyard/orgs', 379 | repos_url: 'https://api.github.com/users/ChrisLidyard/repos', 380 | events_url: 'https://api.github.com/users/ChrisLidyard/events{/privacy}', 381 | received_events_url: 382 | 'https://api.github.com/users/ChrisLidyard/received_events', 383 | type: 'User', 384 | site_admin: false, 385 | }, 386 | { 387 | login: 'flex2016', 388 | id: 26524099, 389 | node_id: 'MDQ6VXNlcjI2NTI0MDk5', 390 | avatar_url: 'https://avatars1.githubusercontent.com/u/26524099?v=4', 391 | gravatar_id: '', 392 | url: 'https://api.github.com/users/flex2016', 393 | html_url: 'https://github.com/flex2016', 394 | followers_url: 'https://api.github.com/users/flex2016/followers', 395 | following_url: 396 | 'https://api.github.com/users/flex2016/following{/other_user}', 397 | gists_url: 'https://api.github.com/users/flex2016/gists{/gist_id}', 398 | starred_url: 'https://api.github.com/users/flex2016/starred{/owner}{/repo}', 399 | subscriptions_url: 'https://api.github.com/users/flex2016/subscriptions', 400 | organizations_url: 'https://api.github.com/users/flex2016/orgs', 401 | repos_url: 'https://api.github.com/users/flex2016/repos', 402 | events_url: 'https://api.github.com/users/flex2016/events{/privacy}', 403 | received_events_url: 404 | 'https://api.github.com/users/flex2016/received_events', 405 | type: 'User', 406 | site_admin: false, 407 | }, 408 | { 409 | login: 'MaurickThom', 410 | id: 43498912, 411 | node_id: 'MDQ6VXNlcjQzNDk4OTEy', 412 | avatar_url: 'https://avatars0.githubusercontent.com/u/43498912?v=4', 413 | gravatar_id: '', 414 | url: 'https://api.github.com/users/MaurickThom', 415 | html_url: 'https://github.com/MaurickThom', 416 | followers_url: 'https://api.github.com/users/MaurickThom/followers', 417 | following_url: 418 | 'https://api.github.com/users/MaurickThom/following{/other_user}', 419 | gists_url: 'https://api.github.com/users/MaurickThom/gists{/gist_id}', 420 | starred_url: 421 | 'https://api.github.com/users/MaurickThom/starred{/owner}{/repo}', 422 | subscriptions_url: 'https://api.github.com/users/MaurickThom/subscriptions', 423 | organizations_url: 'https://api.github.com/users/MaurickThom/orgs', 424 | repos_url: 'https://api.github.com/users/MaurickThom/repos', 425 | events_url: 'https://api.github.com/users/MaurickThom/events{/privacy}', 426 | received_events_url: 427 | 'https://api.github.com/users/MaurickThom/received_events', 428 | type: 'User', 429 | site_admin: false, 430 | }, 431 | { 432 | login: 'iambin', 433 | id: 5101374, 434 | node_id: 'MDQ6VXNlcjUxMDEzNzQ=', 435 | avatar_url: 'https://avatars0.githubusercontent.com/u/5101374?v=4', 436 | gravatar_id: '', 437 | url: 'https://api.github.com/users/iambin', 438 | html_url: 'https://github.com/iambin', 439 | followers_url: 'https://api.github.com/users/iambin/followers', 440 | following_url: 'https://api.github.com/users/iambin/following{/other_user}', 441 | gists_url: 'https://api.github.com/users/iambin/gists{/gist_id}', 442 | starred_url: 'https://api.github.com/users/iambin/starred{/owner}{/repo}', 443 | subscriptions_url: 'https://api.github.com/users/iambin/subscriptions', 444 | organizations_url: 'https://api.github.com/users/iambin/orgs', 445 | repos_url: 'https://api.github.com/users/iambin/repos', 446 | events_url: 'https://api.github.com/users/iambin/events{/privacy}', 447 | received_events_url: 'https://api.github.com/users/iambin/received_events', 448 | type: 'User', 449 | site_admin: false, 450 | }, 451 | { 452 | login: 'OlhaDemydenko', 453 | id: 45002163, 454 | node_id: 'MDQ6VXNlcjQ1MDAyMTYz', 455 | avatar_url: 'https://avatars1.githubusercontent.com/u/45002163?v=4', 456 | gravatar_id: '', 457 | url: 'https://api.github.com/users/OlhaDemydenko', 458 | html_url: 'https://github.com/OlhaDemydenko', 459 | followers_url: 'https://api.github.com/users/OlhaDemydenko/followers', 460 | following_url: 461 | 'https://api.github.com/users/OlhaDemydenko/following{/other_user}', 462 | gists_url: 'https://api.github.com/users/OlhaDemydenko/gists{/gist_id}', 463 | starred_url: 464 | 'https://api.github.com/users/OlhaDemydenko/starred{/owner}{/repo}', 465 | subscriptions_url: 466 | 'https://api.github.com/users/OlhaDemydenko/subscriptions', 467 | organizations_url: 'https://api.github.com/users/OlhaDemydenko/orgs', 468 | repos_url: 'https://api.github.com/users/OlhaDemydenko/repos', 469 | events_url: 'https://api.github.com/users/OlhaDemydenko/events{/privacy}', 470 | received_events_url: 471 | 'https://api.github.com/users/OlhaDemydenko/received_events', 472 | type: 'User', 473 | site_admin: false, 474 | }, 475 | { 476 | login: 'brestby', 477 | id: 14026212, 478 | node_id: 'MDQ6VXNlcjE0MDI2MjEy', 479 | avatar_url: 'https://avatars0.githubusercontent.com/u/14026212?v=4', 480 | gravatar_id: '', 481 | url: 'https://api.github.com/users/brestby', 482 | html_url: 'https://github.com/brestby', 483 | followers_url: 'https://api.github.com/users/brestby/followers', 484 | following_url: 485 | 'https://api.github.com/users/brestby/following{/other_user}', 486 | gists_url: 'https://api.github.com/users/brestby/gists{/gist_id}', 487 | starred_url: 'https://api.github.com/users/brestby/starred{/owner}{/repo}', 488 | subscriptions_url: 'https://api.github.com/users/brestby/subscriptions', 489 | organizations_url: 'https://api.github.com/users/brestby/orgs', 490 | repos_url: 'https://api.github.com/users/brestby/repos', 491 | events_url: 'https://api.github.com/users/brestby/events{/privacy}', 492 | received_events_url: 'https://api.github.com/users/brestby/received_events', 493 | type: 'User', 494 | site_admin: false, 495 | }, 496 | { 497 | login: 'xipeRafa', 498 | id: 43357824, 499 | node_id: 'MDQ6VXNlcjQzMzU3ODI0', 500 | avatar_url: 'https://avatars3.githubusercontent.com/u/43357824?v=4', 501 | gravatar_id: '', 502 | url: 'https://api.github.com/users/xipeRafa', 503 | html_url: 'https://github.com/xipeRafa', 504 | followers_url: 'https://api.github.com/users/xipeRafa/followers', 505 | following_url: 506 | 'https://api.github.com/users/xipeRafa/following{/other_user}', 507 | gists_url: 'https://api.github.com/users/xipeRafa/gists{/gist_id}', 508 | starred_url: 'https://api.github.com/users/xipeRafa/starred{/owner}{/repo}', 509 | subscriptions_url: 'https://api.github.com/users/xipeRafa/subscriptions', 510 | organizations_url: 'https://api.github.com/users/xipeRafa/orgs', 511 | repos_url: 'https://api.github.com/users/xipeRafa/repos', 512 | events_url: 'https://api.github.com/users/xipeRafa/events{/privacy}', 513 | received_events_url: 514 | 'https://api.github.com/users/xipeRafa/received_events', 515 | type: 'User', 516 | site_admin: false, 517 | }, 518 | { 519 | login: 'kotya', 520 | id: 1913221, 521 | node_id: 'MDQ6VXNlcjE5MTMyMjE=', 522 | avatar_url: 'https://avatars3.githubusercontent.com/u/1913221?v=4', 523 | gravatar_id: '', 524 | url: 'https://api.github.com/users/kotya', 525 | html_url: 'https://github.com/kotya', 526 | followers_url: 'https://api.github.com/users/kotya/followers', 527 | following_url: 'https://api.github.com/users/kotya/following{/other_user}', 528 | gists_url: 'https://api.github.com/users/kotya/gists{/gist_id}', 529 | starred_url: 'https://api.github.com/users/kotya/starred{/owner}{/repo}', 530 | subscriptions_url: 'https://api.github.com/users/kotya/subscriptions', 531 | organizations_url: 'https://api.github.com/users/kotya/orgs', 532 | repos_url: 'https://api.github.com/users/kotya/repos', 533 | events_url: 'https://api.github.com/users/kotya/events{/privacy}', 534 | received_events_url: 'https://api.github.com/users/kotya/received_events', 535 | type: 'User', 536 | site_admin: false, 537 | }, 538 | { 539 | login: 'katerinaSo', 540 | id: 37941283, 541 | node_id: 'MDQ6VXNlcjM3OTQxMjgz', 542 | avatar_url: 'https://avatars3.githubusercontent.com/u/37941283?v=4', 543 | gravatar_id: '', 544 | url: 'https://api.github.com/users/katerinaSo', 545 | html_url: 'https://github.com/katerinaSo', 546 | followers_url: 'https://api.github.com/users/katerinaSo/followers', 547 | following_url: 548 | 'https://api.github.com/users/katerinaSo/following{/other_user}', 549 | gists_url: 'https://api.github.com/users/katerinaSo/gists{/gist_id}', 550 | starred_url: 551 | 'https://api.github.com/users/katerinaSo/starred{/owner}{/repo}', 552 | subscriptions_url: 'https://api.github.com/users/katerinaSo/subscriptions', 553 | organizations_url: 'https://api.github.com/users/katerinaSo/orgs', 554 | repos_url: 'https://api.github.com/users/katerinaSo/repos', 555 | events_url: 'https://api.github.com/users/katerinaSo/events{/privacy}', 556 | received_events_url: 557 | 'https://api.github.com/users/katerinaSo/received_events', 558 | type: 'User', 559 | site_admin: false, 560 | }, 561 | { 562 | login: 'JaroslawLis', 563 | id: 37364317, 564 | node_id: 'MDQ6VXNlcjM3MzY0MzE3', 565 | avatar_url: 'https://avatars0.githubusercontent.com/u/37364317?v=4', 566 | gravatar_id: '', 567 | url: 'https://api.github.com/users/JaroslawLis', 568 | html_url: 'https://github.com/JaroslawLis', 569 | followers_url: 'https://api.github.com/users/JaroslawLis/followers', 570 | following_url: 571 | 'https://api.github.com/users/JaroslawLis/following{/other_user}', 572 | gists_url: 'https://api.github.com/users/JaroslawLis/gists{/gist_id}', 573 | starred_url: 574 | 'https://api.github.com/users/JaroslawLis/starred{/owner}{/repo}', 575 | subscriptions_url: 'https://api.github.com/users/JaroslawLis/subscriptions', 576 | organizations_url: 'https://api.github.com/users/JaroslawLis/orgs', 577 | repos_url: 'https://api.github.com/users/JaroslawLis/repos', 578 | events_url: 'https://api.github.com/users/JaroslawLis/events{/privacy}', 579 | received_events_url: 580 | 'https://api.github.com/users/JaroslawLis/received_events', 581 | type: 'User', 582 | site_admin: false, 583 | }, 584 | { 585 | login: 'JustinAnthonyB', 586 | id: 20384195, 587 | node_id: 'MDQ6VXNlcjIwMzg0MTk1', 588 | avatar_url: 'https://avatars2.githubusercontent.com/u/20384195?v=4', 589 | gravatar_id: '', 590 | url: 'https://api.github.com/users/JustinAnthonyB', 591 | html_url: 'https://github.com/JustinAnthonyB', 592 | followers_url: 'https://api.github.com/users/JustinAnthonyB/followers', 593 | following_url: 594 | 'https://api.github.com/users/JustinAnthonyB/following{/other_user}', 595 | gists_url: 'https://api.github.com/users/JustinAnthonyB/gists{/gist_id}', 596 | starred_url: 597 | 'https://api.github.com/users/JustinAnthonyB/starred{/owner}{/repo}', 598 | subscriptions_url: 599 | 'https://api.github.com/users/JustinAnthonyB/subscriptions', 600 | organizations_url: 'https://api.github.com/users/JustinAnthonyB/orgs', 601 | repos_url: 'https://api.github.com/users/JustinAnthonyB/repos', 602 | events_url: 'https://api.github.com/users/JustinAnthonyB/events{/privacy}', 603 | received_events_url: 604 | 'https://api.github.com/users/JustinAnthonyB/received_events', 605 | type: 'User', 606 | site_admin: false, 607 | }, 608 | { 609 | login: 'dundja', 610 | id: 42780673, 611 | node_id: 'MDQ6VXNlcjQyNzgwNjcz', 612 | avatar_url: 'https://avatars1.githubusercontent.com/u/42780673?v=4', 613 | gravatar_id: '', 614 | url: 'https://api.github.com/users/dundja', 615 | html_url: 'https://github.com/dundja', 616 | followers_url: 'https://api.github.com/users/dundja/followers', 617 | following_url: 'https://api.github.com/users/dundja/following{/other_user}', 618 | gists_url: 'https://api.github.com/users/dundja/gists{/gist_id}', 619 | starred_url: 'https://api.github.com/users/dundja/starred{/owner}{/repo}', 620 | subscriptions_url: 'https://api.github.com/users/dundja/subscriptions', 621 | organizations_url: 'https://api.github.com/users/dundja/orgs', 622 | repos_url: 'https://api.github.com/users/dundja/repos', 623 | events_url: 'https://api.github.com/users/dundja/events{/privacy}', 624 | received_events_url: 'https://api.github.com/users/dundja/received_events', 625 | type: 'User', 626 | site_admin: false, 627 | }, 628 | { 629 | login: 'wisemanchris92', 630 | id: 32992448, 631 | node_id: 'MDQ6VXNlcjMyOTkyNDQ4', 632 | avatar_url: 'https://avatars1.githubusercontent.com/u/32992448?v=4', 633 | gravatar_id: '', 634 | url: 'https://api.github.com/users/wisemanchris92', 635 | html_url: 'https://github.com/wisemanchris92', 636 | followers_url: 'https://api.github.com/users/wisemanchris92/followers', 637 | following_url: 638 | 'https://api.github.com/users/wisemanchris92/following{/other_user}', 639 | gists_url: 'https://api.github.com/users/wisemanchris92/gists{/gist_id}', 640 | starred_url: 641 | 'https://api.github.com/users/wisemanchris92/starred{/owner}{/repo}', 642 | subscriptions_url: 643 | 'https://api.github.com/users/wisemanchris92/subscriptions', 644 | organizations_url: 'https://api.github.com/users/wisemanchris92/orgs', 645 | repos_url: 'https://api.github.com/users/wisemanchris92/repos', 646 | events_url: 'https://api.github.com/users/wisemanchris92/events{/privacy}', 647 | received_events_url: 648 | 'https://api.github.com/users/wisemanchris92/received_events', 649 | type: 'User', 650 | site_admin: false, 651 | }, 652 | { 653 | login: '919971603657', 654 | id: 19673568, 655 | node_id: 'MDQ6VXNlcjE5NjczNTY4', 656 | avatar_url: 'https://avatars0.githubusercontent.com/u/19673568?v=4', 657 | gravatar_id: '', 658 | url: 'https://api.github.com/users/919971603657', 659 | html_url: 'https://github.com/919971603657', 660 | followers_url: 'https://api.github.com/users/919971603657/followers', 661 | following_url: 662 | 'https://api.github.com/users/919971603657/following{/other_user}', 663 | gists_url: 'https://api.github.com/users/919971603657/gists{/gist_id}', 664 | starred_url: 665 | 'https://api.github.com/users/919971603657/starred{/owner}{/repo}', 666 | subscriptions_url: 667 | 'https://api.github.com/users/919971603657/subscriptions', 668 | organizations_url: 'https://api.github.com/users/919971603657/orgs', 669 | repos_url: 'https://api.github.com/users/919971603657/repos', 670 | events_url: 'https://api.github.com/users/919971603657/events{/privacy}', 671 | received_events_url: 672 | 'https://api.github.com/users/919971603657/received_events', 673 | type: 'User', 674 | site_admin: false, 675 | }, 676 | ]; 677 | -------------------------------------------------------------------------------- /src/context/mockData.js/mockUser.js: -------------------------------------------------------------------------------- 1 | export default { 2 | login: 'john-smilga', 3 | id: 42133389, 4 | node_id: 'MDQ6VXNlcjQyMTMzMzg5', 5 | avatar_url: 'https://avatars3.githubusercontent.com/u/42133389?v=4', 6 | gravatar_id: '', 7 | url: 'https://api.github.com/users/john-smilga', 8 | html_url: 'https://github.com/john-smilga', 9 | followers_url: 'https://api.github.com/users/john-smilga/followers', 10 | following_url: 11 | 'https://api.github.com/users/john-smilga/following{/other_user}', 12 | gists_url: 'https://api.github.com/users/john-smilga/gists{/gist_id}', 13 | starred_url: 14 | 'https://api.github.com/users/john-smilga/starred{/owner}{/repo}', 15 | subscriptions_url: 'https://api.github.com/users/john-smilga/subscriptions', 16 | organizations_url: 'https://api.github.com/users/john-smilga/orgs', 17 | repos_url: 'https://api.github.com/users/john-smilga/repos', 18 | events_url: 'https://api.github.com/users/john-smilga/events{/privacy}', 19 | received_events_url: 20 | 'https://api.github.com/users/john-smilga/received_events', 21 | type: 'User', 22 | site_admin: false, 23 | name: 'John Smilga', 24 | company: 'Coding Addict', 25 | blog: 'www.johnsmilga.com', 26 | location: 'Los Angeles', 27 | email: null, 28 | hireable: null, 29 | bio: 'Creator of Coding Addict', 30 | twitter_username: 'john_smilga', 31 | public_repos: 152, 32 | public_gists: 0, 33 | followers: 1495, 34 | following: 0, 35 | created_at: '2018-08-06T06:48:23Z', 36 | updated_at: '2020-07-08T05:01:32Z', 37 | }; 38 | -------------------------------------------------------------------------------- /src/images/login-img.svg: -------------------------------------------------------------------------------- 1 | developer activity -------------------------------------------------------------------------------- /src/images/preloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-search-github-users/bad10334cdf5b057f9da9f3a70cf97dff4f630bb/src/images/preloader.gif -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | =============== 3 | Fonts 4 | =============== 5 | */ 6 | @import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap'); 7 | 8 | /* 9 | =============== 10 | Variables 11 | =============== 12 | */ 13 | 14 | :root { 15 | /* dark shades of primary color*/ 16 | --clr-primary-1: hsl(185, 91%, 17%); 17 | --clr-primary-2: hsl(185, 84%, 25%); 18 | --clr-primary-3: hsl(185, 81%, 29%); 19 | --clr-primary-4: hsl(185, 77%, 34%); 20 | /* primary/main color */ 21 | --clr-primary-5: hsl(185, 62%, 45%); 22 | /* lighter shades of primary color */ 23 | --clr-primary-6: hsl(185, 57%, 50%); 24 | --clr-primary-7: hsl(184, 65%, 59%); 25 | --clr-primary-8: hsl(184, 80%, 74%); 26 | --clr-primary-9: hsl(185, 94%, 87%); 27 | --clr-primary-10: hsl(186, 100%, 94%); 28 | /* darkest grey - used for headings */ 29 | --clr-grey-1: hsl(209, 61%, 16%); 30 | --clr-grey-2: hsl(211, 39%, 23%); 31 | --clr-grey-3: hsl(209, 34%, 30%); 32 | --clr-grey-4: hsl(209, 28%, 39%); 33 | /* grey used for paragraphs */ 34 | --clr-grey-5: hsl(210, 22%, 49%); 35 | --clr-grey-6: hsl(209, 23%, 60%); 36 | --clr-grey-7: hsl(211, 27%, 70%); 37 | --clr-grey-8: hsl(210, 31%, 80%); 38 | --clr-grey-9: hsl(212, 33%, 89%); 39 | --clr-grey-10: #f1f5f8; 40 | --clr-white: #fff; 41 | --clr-red-dark: hsl(360, 67%, 44%); 42 | --clr-red-light: hsl(360, 71%, 66%); 43 | --clr-green-dark: hsl(125, 67%, 44%); 44 | --clr-green-light: hsl(125, 71%, 66%); 45 | --clr-black: #222; 46 | --ff-primary: 'Roboto', sans-serif; 47 | --ff-secondary: 'Open Sans', sans-serif; 48 | --transition: all 0.3s linear; 49 | --spacing: 0.1rem; 50 | --radius: 0.25rem; 51 | --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); 52 | --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); 53 | --max-width: 1170px; 54 | --fixed-width: 620px; 55 | } 56 | /* 57 | =============== 58 | Global Styles 59 | =============== 60 | */ 61 | 62 | *, 63 | ::after, 64 | ::before { 65 | margin: 0; 66 | padding: 0; 67 | box-sizing: border-box; 68 | } 69 | body { 70 | font-family: var(--ff-secondary); 71 | background: var(--clr-grey-10); 72 | color: var(--clr-grey-1); 73 | line-height: 1.5; 74 | font-size: 0.875rem; 75 | } 76 | ul { 77 | list-style-type: none; 78 | } 79 | a { 80 | text-decoration: none; 81 | } 82 | img { 83 | width: 100%; 84 | display: block; 85 | } 86 | 87 | h1, 88 | h2, 89 | h3, 90 | h4 { 91 | letter-spacing: var(--spacing); 92 | text-transform: capitalize; 93 | line-height: 1.25; 94 | margin-bottom: 0.75rem; 95 | font-family: var(--ff-primary); 96 | } 97 | h1 { 98 | font-size: 3rem; 99 | } 100 | h2 { 101 | font-size: 2rem; 102 | } 103 | h3 { 104 | font-size: 1.25rem; 105 | } 106 | h4 { 107 | font-size: 0.875rem; 108 | } 109 | p { 110 | margin-bottom: 1.25rem; 111 | color: var(--clr-grey-5); 112 | } 113 | @media screen and (min-width: 800px) { 114 | h1 { 115 | font-size: 4rem; 116 | } 117 | h2 { 118 | font-size: 2.5rem; 119 | } 120 | h3 { 121 | font-size: 1.75rem; 122 | } 123 | h4 { 124 | font-size: 1rem; 125 | } 126 | body { 127 | font-size: 1rem; 128 | } 129 | h1, 130 | h2, 131 | h3, 132 | h4 { 133 | line-height: 1; 134 | } 135 | } 136 | /* global classes */ 137 | 138 | .btn { 139 | text-transform: uppercase; 140 | background: var(--clr-primary-5); 141 | color: var(--clr-primary-10); 142 | padding: 0.375rem 0.75rem; 143 | letter-spacing: var(--spacing); 144 | display: inline-block; 145 | font-weight: 400; 146 | transition: var(--transition); 147 | font-size: 0.875rem; 148 | border: 2px solid transparent; 149 | cursor: pointer; 150 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); 151 | border-radius: var(--radius); 152 | } 153 | .btn:hover { 154 | background: var(--clr-primary-8); 155 | color: var(--clr-primary-1); 156 | } 157 | 158 | /* section */ 159 | .section { 160 | padding: 1rem 0; 161 | position: relative; 162 | } 163 | 164 | .section-center { 165 | width: 90vw; 166 | margin: 0 auto; 167 | max-width: 1170px; 168 | } 169 | @media screen and (min-width: 992px) { 170 | .section-center { 171 | width: 95vw; 172 | } 173 | } 174 | .loading-img { 175 | width: 20rem; 176 | height: 20rem; 177 | display: block; 178 | margin: 0 auto; 179 | margin-top: 10rem; 180 | } 181 | main { 182 | padding-bottom: 2rem; 183 | } 184 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | import { GithubProvider } from './context/context'; 7 | import { Auth0Provider } from '@auth0/auth0-react'; 8 | const root = ReactDOM.createRoot(document.getElementById('root')); 9 | root.render( 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | 24 | // If you want your app to work offline and load faster, you can change 25 | // unregister() to register() below. Note this comes with some pitfalls. 26 | // Learn more about service workers: https://bit.ly/CRA-PWA 27 | serviceWorker.unregister(); 28 | -------------------------------------------------------------------------------- /src/pages/AuthWrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useAuth0 } from '@auth0/auth0-react'; 3 | import loadingGif from '../images/preloader.gif'; 4 | import styled from 'styled-components'; 5 | function AuthWrapper({ children }) { 6 | const { isLoading, error } = useAuth0(); 7 | if (isLoading) { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | if (error) { 15 | return ( 16 | 17 | {error.message} 18 | 19 | ); 20 | } 21 | return <>{children}>; 22 | } 23 | 24 | const Wrapper = styled.section` 25 | min-height: 100vh; 26 | display: grid; 27 | place-items: center; 28 | img { 29 | width: 150px; 30 | } 31 | `; 32 | 33 | export default AuthWrapper; 34 | -------------------------------------------------------------------------------- /src/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Info, Repos, User, Search, Navbar } from '../components'; 3 | import loadingImage from '../images/preloader.gif'; 4 | import { GithubContext } from '../context/context'; 5 | const Dashboard = () => { 6 | const { isLoading } = React.useContext(GithubContext); 7 | if (isLoading) { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | } 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | }; 26 | 27 | export default Dashboard; 28 | -------------------------------------------------------------------------------- /src/pages/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { Link } from 'react-router-dom'; 4 | const Error = () => { 5 | return ( 6 | 7 | 8 | 404 9 | sorry, the page you tried cannot be found 10 | 11 | back home 12 | 13 | 14 | 15 | ); 16 | }; 17 | const Wrapper = styled.section` 18 | min-height: 100vh; 19 | display: grid; 20 | place-items: center; 21 | background: var(--clr-primary-10); 22 | text-align: center; 23 | h1 { 24 | font-size: 10rem; 25 | } 26 | h3 { 27 | color: var(--clr-grey-3); 28 | margin-bottom: 1.5rem; 29 | } 30 | `; 31 | export default Error; 32 | -------------------------------------------------------------------------------- /src/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useAuth0 } from '@auth0/auth0-react'; 3 | import styled from 'styled-components'; 4 | import loginImg from '../images/login-img.svg'; 5 | const Login = () => { 6 | const { loginWithRedirect } = useAuth0(); 7 | return ( 8 | 9 | 10 | 11 | github user 12 | 13 | Log In / Sign Up 14 | 15 | 16 | 17 | ); 18 | }; 19 | const Wrapper = styled.section` 20 | min-height: 100vh; 21 | display: grid; 22 | place-items: center; 23 | .container { 24 | width: 90vw; 25 | max-width: 600px; 26 | text-align: center; 27 | } 28 | img { 29 | margin-bottom: 2rem; 30 | } 31 | h1 { 32 | margin-bottom: 1.5rem; 33 | } 34 | `; 35 | export default Login; 36 | -------------------------------------------------------------------------------- /src/pages/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Navigate } from 'react-router-dom'; 3 | import { useAuth0 } from '@auth0/auth0-react'; 4 | 5 | const PrivateRoute = ({ children }) => { 6 | const { isAuthenticated, user } = useAuth0(); 7 | const isUser = isAuthenticated && user; 8 | if (!isUser) { 9 | return ; 10 | } 11 | return children; 12 | }; 13 | export default PrivateRoute; 14 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import Dashboard from "./Dashboard"; 2 | import Login from "./Login"; 3 | import AuthWrapper from "./AuthWrapper"; 4 | import PrivateRoute from "./PrivateRoute"; 5 | import Error from "./Error"; 6 | 7 | export { Dashboard, Login, AuthWrapper, PrivateRoute, Error }; 8 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | --------------------------------------------------------------------------------
@{twitter_username || 'john doe'}
{bio}
30 | {company} 31 |
33 | {location || 'earth'} 34 |
{label}
{error.msg}