diff --git a/solutions/src/error-boundaries/api/repos.js b/solutions/src/error-boundaries/api/repos.js
new file mode 100644
index 0000000..1a3080a
--- /dev/null
+++ b/solutions/src/error-boundaries/api/repos.js
@@ -0,0 +1,23 @@
+export const getRepos = username => {
+ return fetch(`https://api.github.com/users/${username}/repos?sort=updated`)
+ .then(response => response.json())
+ .then(data => {
+ let repos = data.map(repo => {
+ return {
+ id: repo.id,
+ name: repo.name,
+ stars: repo.stargazers_count,
+ description: repo.description,
+ url: repo.html_url
+ };
+ });
+ repos = repos.sort((a, b) => b.stars - a.stars).slice(0, 5);
+ return repos;
+ })
+ .catch(error => {
+ /* Error handling */
+ return {
+ error
+ };
+ });
+};
diff --git a/solutions/src/error-boundaries/api/user.js b/solutions/src/error-boundaries/api/user.js
new file mode 100644
index 0000000..7eb946a
--- /dev/null
+++ b/solutions/src/error-boundaries/api/user.js
@@ -0,0 +1,16 @@
+export const getUser = username => {
+ return fetch(`https://api.github.com/users/${username}`)
+ .then(response => response.json())
+ .then(data => {
+ return {
+ username: data.login,
+ name: data.name,
+ photo: data.avatar_url,
+ bio: data.bio || "no description",
+ url: data.html_url
+ };
+ })
+ .catch(error => {
+ return { error };
+ });
+};
diff --git a/solutions/src/error-boundaries/components/common/errorImage.js b/solutions/src/error-boundaries/components/common/errorImage.js
new file mode 100644
index 0000000..432ae59
--- /dev/null
+++ b/solutions/src/error-boundaries/components/common/errorImage.js
@@ -0,0 +1,6 @@
+import React from "react";
+import errorImage from "../../img/error.jpg";
+
+const ErrorImage = () =>
;
+
+export default ErrorImage;
diff --git a/solutions/src/error-boundaries/components/common/link.js b/solutions/src/error-boundaries/components/common/link.js
new file mode 100644
index 0000000..3ce633d
--- /dev/null
+++ b/solutions/src/error-boundaries/components/common/link.js
@@ -0,0 +1,21 @@
+import React from "react";
+import styled from "styled-components";
+
+import A from "../library/anchor";
+import Blink from "../library/blink";
+
+const Loading = styled(A)`
+ background: #6ed396;
+ width: 40%;
+ height: 22px;
+ margin-top: 10px;
+ &::before {
+ content: "x";
+ }
+ animation: ${Blink} 2s linear infinite;
+`;
+
+export default props => {
+ if (props.url) return {props.children};
+ else return ;
+};
diff --git a/solutions/src/error-boundaries/components/common/logo.js b/solutions/src/error-boundaries/components/common/logo.js
new file mode 100644
index 0000000..16b3796
--- /dev/null
+++ b/solutions/src/error-boundaries/components/common/logo.js
@@ -0,0 +1,5 @@
+import React from "react";
+import githubLogo from "../../img/github-logo.png";
+const Logo = () =>
;
+
+export default Logo;
diff --git a/solutions/src/error-boundaries/components/common/nav.js b/solutions/src/error-boundaries/components/common/nav.js
new file mode 100644
index 0000000..98a943a
--- /dev/null
+++ b/solutions/src/error-boundaries/components/common/nav.js
@@ -0,0 +1,41 @@
+import React from "react";
+import styled, { injectGlobal } from "styled-components";
+import Logo from "./logo";
+import Helmet from "react-helmet";
+
+injectGlobal`
+ body {
+ margin: 0;
+ padding: 0;
+ background: #EEE;
+ font-family: 'Nunito', sans-serif;
+ }
+`;
+
+const NavBar = styled.div`
+ height: 30px;
+ padding: 10px;
+ background: #fff;
+ border-bottom: 1px solid #ddd;
+ text-align: center;
+ > img {
+ height: 30px;
+ }
+`;
+
+const Nav = () => (
+
+
+
+
+);
+
+export default Nav;
diff --git a/solutions/src/error-boundaries/components/library/anchor.js b/solutions/src/error-boundaries/components/library/anchor.js
new file mode 100644
index 0000000..7a60833
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/anchor.js
@@ -0,0 +1,10 @@
+import styled from "styled-components";
+
+const A = styled.a`
+ text-decoration: none;
+ color: #6ed396;
+ display: inline-block;
+ width: "20px";
+`;
+
+export default A;
diff --git a/solutions/src/error-boundaries/components/library/avatar.js b/solutions/src/error-boundaries/components/library/avatar.js
new file mode 100644
index 0000000..f563d31
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/avatar.js
@@ -0,0 +1,15 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Avatar = styled.span`
+ display: inline-block;
+ height: 100px;
+ width: 100px;
+ border-radius: 50%;
+ border: 5px solid #EEE;
+ background-color: #EEE;
+ ${props => (props.src ? `background-image: url(${props.src})` : '')};
+ background-size: cover;
+`
+
+export default props =>
diff --git a/solutions/src/error-boundaries/components/library/blink.js b/solutions/src/error-boundaries/components/library/blink.js
new file mode 100644
index 0000000..9a8f46d
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/blink.js
@@ -0,0 +1,9 @@
+import { keyframes } from 'styled-components'
+
+const Blink = keyframes`
+ 0% {opacity: 0.1}
+ 50% {opacity: 0.3}
+ 100% {opacity: 0.1}
+`
+
+export default Blink
diff --git a/solutions/src/error-boundaries/components/library/button.js b/solutions/src/error-boundaries/components/library/button.js
new file mode 100644
index 0000000..daaa889
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/button.js
@@ -0,0 +1,18 @@
+import styled from "styled-components";
+
+const Button = styled.button`
+ background: #0eb550;
+ color: #fff;
+ border: 1px solid #1d99bd;
+ border-radius: 2px;
+ padding: 10px;
+ margin-top: 20px;
+ width: 100%;
+ outline: none;
+ font-size: 16px;
+ &:active {
+ background: #6ed396;
+ }
+`;
+
+export default Button;
diff --git a/solutions/src/error-boundaries/components/library/card.js b/solutions/src/error-boundaries/components/library/card.js
new file mode 100644
index 0000000..3070808
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/card.js
@@ -0,0 +1,10 @@
+import styled from 'styled-components'
+
+const Card = styled.div`
+ background: #FFF;
+ border: 1px solid #DDD;
+ border-radius: 2px;
+ padding: 10px;
+`
+
+export default Card
diff --git a/solutions/src/error-boundaries/components/library/clear.js b/solutions/src/error-boundaries/components/library/clear.js
new file mode 100644
index 0000000..67b0b93
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/clear.js
@@ -0,0 +1,7 @@
+import styled from 'styled-components'
+
+const Clear = styled.div`
+ clear: both;
+`
+
+export default Clear
diff --git a/solutions/src/error-boundaries/components/library/input.js b/solutions/src/error-boundaries/components/library/input.js
new file mode 100644
index 0000000..00ff788
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/input.js
@@ -0,0 +1,18 @@
+import styled from "styled-components";
+
+const Input = styled.input`
+ background: #fff;
+ border: 1px solid #ddd;
+ border-radius: 2px;
+ padding: 10px;
+ width: calc(100% - 20px);
+ outline: none;
+ font-size: 16px;
+
+ &:hover,
+ &:focus {
+ border-color: #6ed396;
+ }
+`;
+
+export default Input;
diff --git a/solutions/src/error-boundaries/components/library/label.js b/solutions/src/error-boundaries/components/library/label.js
new file mode 100644
index 0000000..a59b655
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/label.js
@@ -0,0 +1,10 @@
+import styled from "styled-components";
+
+const Label = styled.div`
+ height: 30px;
+ padding: 10px;
+ text-align: center;
+ color: ${props => props.color || "#0EB550"};
+`;
+
+export default Label;
diff --git a/solutions/src/error-boundaries/components/library/star.js b/solutions/src/error-boundaries/components/library/star.js
new file mode 100644
index 0000000..015aeac
--- /dev/null
+++ b/solutions/src/error-boundaries/components/library/star.js
@@ -0,0 +1,11 @@
+import styled from 'styled-components'
+
+const Star = styled.span`
+ margin: 10px;
+ &::after {
+ content: ' \\2605';
+ color: gold;
+ }
+`
+
+export default Star
diff --git a/solutions/src/error-boundaries/img/error.jpg b/solutions/src/error-boundaries/img/error.jpg
new file mode 100644
index 0000000..7f45d01
Binary files /dev/null and b/solutions/src/error-boundaries/img/error.jpg differ
diff --git a/solutions/src/error-boundaries/img/github-logo.png b/solutions/src/error-boundaries/img/github-logo.png
new file mode 100644
index 0000000..8f109fd
Binary files /dev/null and b/solutions/src/error-boundaries/img/github-logo.png differ
diff --git a/solutions/src/error-boundaries/index.js b/solutions/src/error-boundaries/index.js
new file mode 100644
index 0000000..53932a3
--- /dev/null
+++ b/solutions/src/error-boundaries/index.js
@@ -0,0 +1,15 @@
+import React, { Component } from "react";
+import Profile from "./modules/Profile";
+import ErrorBoundary from "./modules/ErrorBoundary";
+
+class App extends Component {
+ render() {
+ return (
+
+
+
+ );
+ }
+}
+
+export default App;
diff --git a/solutions/src/error-boundaries/modules/ErrorBoundary/index.js b/solutions/src/error-boundaries/modules/ErrorBoundary/index.js
new file mode 100644
index 0000000..b370586
--- /dev/null
+++ b/solutions/src/error-boundaries/modules/ErrorBoundary/index.js
@@ -0,0 +1,47 @@
+import React, { Component } from "react";
+import styled from "styled-components";
+import ErrorImage from "../../components/common/errorImage";
+import Card from "../../components/library/card";
+import Button from "../../components/library/button";
+import Label from "../../components/library/label";
+const ErrorCard = styled(Card)`
+ text-align: center;
+ box-sizing: border-box;
+ margin: 20px auto 0;
+ padding: 30px 50px;
+ width: 500px;
+ min-height: 100px;
+ color: #000000;
+
+ @media (max-width: 600px) {
+ width: 90%;
+ }
+`;
+
+export default class ErrorBoundary extends Component {
+ state = { hasError: false };
+
+ componentDidCatch(error, info) {
+ // Display fallback UI
+ this.setState({ hasError: true });
+ // You can also log the error to an error reporting service
+ // logErrorToMyService(error, info);
+ }
+ reloadPage = () => {
+ window.location.reload();
+ };
+ render() {
+ console.log("chekc here", this.state);
+ if (this.state.hasError) {
+ // You can render any custom fallback UI
+ return (
+
+
+
+
+
+ );
+ }
+ return this.props.children;
+ }
+}
diff --git a/solutions/src/error-boundaries/modules/Profile/description.js b/solutions/src/error-boundaries/modules/Profile/description.js
new file mode 100644
index 0000000..f3efc3f
--- /dev/null
+++ b/solutions/src/error-boundaries/modules/Profile/description.js
@@ -0,0 +1,8 @@
+import React from 'react'
+import styled from 'styled-components'
+const Description = styled.div`
+ color: #999;
+ font-size: 12px;
+`
+
+export default props => {props.content}
diff --git a/solutions/src/error-boundaries/modules/Profile/index.js b/solutions/src/error-boundaries/modules/Profile/index.js
new file mode 100644
index 0000000..2c0e7e9
--- /dev/null
+++ b/solutions/src/error-boundaries/modules/Profile/index.js
@@ -0,0 +1,52 @@
+import React, { Component, Fragment } from "react";
+import { getRepos } from "../../api/repos";
+import { getUser } from "../../api/user";
+import ProfileInput from "./profile-input";
+import UserProfile from "./profile";
+import Repositories from "./repositories";
+import Card from "../../components/library/card";
+import Nav from "../../components/common/nav";
+import styled from "styled-components";
+
+const RepositoriesCard = styled(Card)`
+ text-align: center;
+ box-sizing: border-box;
+ margin: 20px auto 0;
+ padding: 30px 50px;
+ width: 500px;
+ min-height: 100px;
+ color: #000000;
+
+ @media (max-width: 600px) {
+ width: 90%;
+ }
+`;
+export default class Profile extends Component {
+ state = {
+ userRepos: [{}, {}, {}, {}, {}],
+ userProfile: {}
+ };
+ getUserInfo = userName => {
+ getUser(userName).then(userProfile => this.setState({ userProfile }));
+ getRepos(userName).then(userRepos => this.setState({ userRepos }));
+ };
+ render() {
+ const { userRepos, userProfile } = this.state;
+ let RepositoriesData = (
+
+
+
+
+ );
+
+ if (!userProfile.url) {
+ RepositoriesData = No Data Found..;
+ }
+ return (
+
+
+ {RepositoriesData}
+
+ );
+ }
+}
diff --git a/solutions/src/error-boundaries/modules/Profile/profile-input.js b/solutions/src/error-boundaries/modules/Profile/profile-input.js
new file mode 100644
index 0000000..f8b5926
--- /dev/null
+++ b/solutions/src/error-boundaries/modules/Profile/profile-input.js
@@ -0,0 +1,67 @@
+import React from "react";
+import styled from "styled-components";
+import Card from "../../components/library/card";
+import Input from "../../components/library/input";
+import Button from "../../components/library/button";
+import Logo from "../../components/common/logo";
+
+const ProfileInput = styled(Card)`
+ text-align: center;
+ box-sizing: border-box;
+ width: 500px;
+ margin: 20px auto 0;
+ padding: 30px 50px;
+ min-height: 100px;
+
+ @media (max-width: 600px) {
+ width: 90%;
+ }
+
+ > input {
+ margin-top: 30px;
+ }
+
+ > a {
+ width: 100%;
+ margin-top: 10px;
+ }
+`;
+
+export default class extends React.Component {
+ /* Set initial state */
+ state = { username: "" };
+ /* Change state on change */
+ onChange = event => {
+ this.setState({ username: event.target.value });
+ };
+ /* Trigger submit on enter key */
+ onKeyUp = event => {
+ if (event.which === 13) this.setState({ username: event.target.value });
+ };
+ /* Pick value from input on focus */
+ onFocus = event => {
+ this.setState({ username: event.target.value });
+ };
+ getUserInfo = () => {
+ this.props.getUserInfo(this.state.username);
+ };
+ /*
+ Compose presentation components inside our
+ ProfileInput components
+ */
+ render() {
+ return (
+
+
+
+
+
+ );
+ }
+}
diff --git a/solutions/src/error-boundaries/modules/Profile/profile.js b/solutions/src/error-boundaries/modules/Profile/profile.js
new file mode 100644
index 0000000..ebc76d7
--- /dev/null
+++ b/solutions/src/error-boundaries/modules/Profile/profile.js
@@ -0,0 +1,30 @@
+import React from "react";
+import styled from "styled-components";
+import Card from "../../components/library/card";
+import Link from "../../components/common/link";
+
+import Avatar from "../../components/library/avatar";
+import Description from "./description";
+
+const ProfileCard = styled(Card)`
+ text-align: center;
+ box-sizing: border-box;
+ margin: 50px auto 0;
+ min-height: 180px;
+
+ @media (max-width: 600px) {
+ width: 90%;
+ }
+`;
+const Profile = ({ userProfile }) => {
+ return (
+
+
+
+ {userProfile.name}
+
+
+
+ );
+};
+export default Profile;
diff --git a/solutions/src/error-boundaries/modules/Profile/repositories.js b/solutions/src/error-boundaries/modules/Profile/repositories.js
new file mode 100644
index 0000000..35680d5
--- /dev/null
+++ b/solutions/src/error-boundaries/modules/Profile/repositories.js
@@ -0,0 +1,40 @@
+import React from "react";
+import styled from "styled-components";
+import Card from "../../components/library/card";
+import Star from "../../components/library/star";
+import Clear from "../../components/library/clear";
+import Link from "../../components/common/link";
+
+const RepoList = styled(Card)`
+ margin: 10px auto;
+ box-sizing: border-box;
+ min-height: 215px;
+
+ @media (max-width: 600px) {
+ width: 90%;
+ }
+`;
+
+const Repo = styled.div`
+ color: #777;
+ > a {
+ float: left;
+ margin-top: 10px;
+ }
+ > span {
+ float: right;
+ }
+`;
+
+export default props => (
+
+ {props.repos &&
+ props.repos.map((repo, index) => (
+
+ {repo.name}
+ {repo.stars}
+
+
+ ))}
+
+);