Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
package-lock.json
node_modules
771 changes: 333 additions & 438 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"axios": "^0.21.1",
"axios": "^0.21.4",
"bootstrap": "^4.6.0",
"clsx": "^1.1.1",
"prop-types": "^15.7.2",
Expand All @@ -21,6 +21,7 @@
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"sass": "^1.32.11",
"sass-loader": "^12.1.0",
"web-vitals": "^1.0.1"
},
"devDependencies": {
Expand All @@ -44,7 +45,7 @@
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.2.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-markdown": "^2.0.0",
"eslint-plugin-markdown": "^2.2.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
Expand Down
18 changes: 17 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Character from "./pages/Character";
import Episode from "./pages/Episode";

import Home from "./pages/Home";
import Location from "./pages/Location";

function App() {
return <Home />;
return (
<BrowserRouter>
<Switch>
<Route exact path="/episode/:episodeId" component={Episode} />
<Route exact path="/character/:characterId" component={Character} />
<Route exact path="/location/:locationId" component={Location} />

<Route path="/">
<Home />
</Route>
</Switch>
</BrowserRouter>
);
}

export default App;
17 changes: 11 additions & 6 deletions src/components/CharacterCard/CharacterCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@ import "./CharacterCard.scss";
import * as routes from "../../constants/routes";

function CharacterCard({ id, name, image, species, status, origin, location }) {
let locationId = origin.url.split("/");
locationId = locationId[locationId.length - 1];
return (
<div className="col col-12 col-sm-6 col-xl-3 CharacterCard">
<img className="CharacterCard__img" src={image} alt="" />
<Link to={`${routes.CHARACTER}/${id}`}>
<h3 className="CharacterCard__name h4">{name}</h3>
</Link>
<div className="CharacterCard__meta">
<Link
className="CharacterCard__meta-item"
to={`${routes.LOCATION}/${id}`}
>
{origin.name}
</Link>
{locationId && (
<Link
className="CharacterCard__meta-item"
to={`${routes.LOCATION}/${locationId}`}
>
{origin.name}
</Link>
)}
{!locationId && <p>Unknown</p>}
<p className="CharacterCard__meta-item">|</p>
<p className="CharacterCard__meta-item">{status}</p>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/constants/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const EPISODES = "/episodes";
export const EPISODE = "/episode";
export const LOCATION = "/location";
export const CHARACTER = "/character";
export const API = "https://rickandmortyapi.com/api";
103 changes: 103 additions & 0 deletions src/pages/Character/Character.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import axios from "axios";
import React, { Component } from "react";
import EpisodeCard from "../../components/EpisodeCard";
import Layout from "../../components/Layout";
import { API, CHARACTER } from "../../constants/routes";

class Character extends Component {
constructor(props) {
super(props);
this.state = {
isLoaded: false,
character: {},
episodes: [],
};
}

componentDidMount() {
this.loadCharacterEpisodes();
}

loadCharacterEpisodes() {
const { match } = this.props;
const characterId = match.params.characterId;
axios.get(`${API}${CHARACTER}/${characterId}`).then((data) => {
this.setState({ character: data.data });
const episodeUrls = data.data.episode;
axios
.all(episodeUrls.map((episodeUrl) => axios.get(episodeUrl)))
.then((dataCollection) => {
const episodes = dataCollection.map(
(collectionData) => collectionData.data,
);
this.setState({ episodes: episodes, isLoaded: true });
});
});
}

render() {
const { character, isLoaded, episodes } = this.state;

return (
<>
<Layout>
{isLoaded && (
<div>
<div className="row">
<div className="col col-6 col-sm-6 col-xl-3 CharacterCard">
<img
className="CharacterCard__img"
src={character.image}
alt=""
/>
</div>
<div className="pl-5 col col-6 ">
<h3 className="CharacterCard__name h4">{character.name}</h3>
<hr />
<h4 className="CharacterCard__name h6">CHARACTER</h4>
<div className="CharacterCard__meta">
<p className="CharacterCard__meta-item">
{character.origin.name} | {character.status}
</p>
</div>
<div className="row m-0">
<div>
<h4 className="CharacterCard__name h6">ORIGIN</h4>
<div className="CharacterCard__meta">
<p className="CharacterCard__meta-item">
{character.origin.name}
</p>
</div>
</div>
<div>
<h4 className="CharacterCard__name h6">LOCATION</h4>
<div className="CharacterCard__meta">
<p className="CharacterCard__meta-item">
{character.location.name}
</p>
</div>
</div>
</div>
</div>
</div>

<div className="row">
{episodes.map((episode) => (
<EpisodeCard
key={episode.id}
id={episode.id}
name={episode.name}
airDate={episode.air_date}
episode={episode.episode}
/>
))}
</div>
</div>
)}
</Layout>
</>
);
}
}

export default Character;
1 change: 1 addition & 0 deletions src/pages/Character/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./Character";
137 changes: 112 additions & 25 deletions src/pages/Episode/Episode.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,127 @@
import axios from "axios";
import React, { Component } from "react";

import Layout from "../../components/Layout";
// import CharacterCard from "../../components/CharacterCard";
import CharacterCard from "../../components/CharacterCard";
import { API, EPISODE } from "../../constants/routes";

class Episode extends Component {
constructor(props) {
super(props);

this.state = {};
// episode: null,
// characters: [],
// hasLoaded: false,
// hasError: false,
// errorMessage: null,
this.state = {
episode: null,
characters: [],
hasLoaded: false,
hasError: false,
errorMessage: null,
};
}

componentDidMount() {
this.loadCharacters();
}

loadCharacters() {
const { match } = this.props;
const { episodeId } = match.params;
const charactersUrl = `${API}${EPISODE}/${episodeId}`;
this.fetchEpisodeCharacters(charactersUrl);
}

fetchEpisodeCharacters(charactersUrl) {
axios
.get(charactersUrl)
.then((apiEpisode) => {
this.setState({
episode: {
id: apiEpisode.data.id,
name: apiEpisode.data.name,
episode: apiEpisode.data.episode,
air_date: apiEpisode.data.air_date,
},
});

const characterUrls = apiEpisode.data.characters;
this.fetchCharacters(characterUrls);
})
.catch((error) => {
this.setState({
hasLoaded: false,
hasError: true,
errorMessage: error,
});
});
}

fetchCharacters(characterUrls) {
axios
.all(
characterUrls.map((char) => {
return axios.get(char);
}),
)
.then((data) => {
const charactersArr = data.map((characterData) => {
return characterData.data;
});
this.setState({
characters: charactersArr,
hasLoaded: true,
hasError: false,
});
})
.catch((error) => {
this.setState({
hasLoaded: false,
hasError: true,
errorMessage: error,
});
});
}

render() {
const {
episode,
characters,
hasLoaded,
hasError,
errorMessage,
} = this.state;

return (
<Layout>
<section className="row">
<div className="col col-12">
{/* {characters.map((character) => (
<CharacterCard
key={character.id}
id={character.id}
name={character.name}
image={character.image}
species={character.species}
status={character.status}
origin={character.origin}
location={character.location}
/>
))} */}
</div>
</section>
</Layout>
<>
<Layout>
<section className="row">
{hasError && <div>{errorMessage}</div>}
{hasLoaded && (
<div className="col col-12 p-0">
<div className="col col-12 col-sm-6 col-xl-4 EpisodeCard">
<h3 className="Episode__name h5">{episode.name}</h3>
<div className="Episode__meta">
<p className="Episode__meta-item">{episode.episode}</p>
<p className="Episode__meta-item">|</p>
<p className="Episode__meta-item">{episode.air_date}</p>
</div>
</div>
</div>
)}
{hasLoaded &&
characters.map((character) => (
<CharacterCard
key={character.id}
id={character.id}
name={character.name}
image={character.image}
species={character.species}
status={character.status}
origin={character.origin}
location={character.location}
/>
))}
</section>
</Layout>
</>
);
}
}
Expand Down
Loading