From 592807a07f61e4e5508f936131f65723e3a31f6e Mon Sep 17 00:00:00 2001 From: sanadriu Date: Fri, 1 Oct 2021 15:41:14 +0200 Subject: [PATCH 01/21] Home and Episode content loading completed --- .eslintignore | 1 - .eslintrc.js | 63 --------------- .prettierrc | 19 ----- src/App.js | 17 +++- src/api/requests.js | 25 ++++++ src/constants/routes.js | 1 + src/pages/Episode/Episode.js | 106 +++++++++++++++++-------- src/pages/Home/Home.js | 145 +++++++++++++++++++++++------------ 8 files changed, 210 insertions(+), 167 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.js delete mode 100644 .prettierrc create mode 100644 src/api/requests.js diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index d8b83df..0000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -package-lock.json diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 3480d73..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,63 +0,0 @@ -module.exports = { - env: { - browser: true, - es2021: true, - node: true, - jest: true, - jquery: true, - "jest/globals": true, - }, - extends: [ - "airbnb", - "eslint:recommended", - "plugin:compat/recommended", - "plugin:react/recommended", - "plugin:jest/recommended", - "prettier", - "prettier/prettier", - "plugin:import/errors", - "plugin:import/warnings", - "plugin:jsx-a11y/recommended", - "react-app", - "react-app/jest", - ], - parser: "@babel/eslint-parser", - parserOptions: { - ecmaVersion: 12, - sourceType: "module", - requireConfigFile: "false", - jsx: true, - }, - plugins: [ - "@html-eslint", - "jest", - "react", - "jsx-a11y", - "markdown", - "react-hooks", - "import", - ], - settings: { - react: { - version: "detect", - }, - }, - overrides: [ - { - files: ["*.html"], - parser: "@html-eslint/parser", - extends: ["plugin:@html-eslint/recommended"], - }, - ], - rules: { - "react/jsx-filename-extension": "off", - "import/prefer-default-export": "off", - "prefer-destructuring": "off", - "object-shorthand": "off", - "react/jsx-props-no-spreading": "off", - "arrow-body-style": "off", - "no-underscore-dangle": "off", - "react/forbid-prop-types": "off", - "react/prop-types": "off", - }, -}; diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 8c4b2fa..0000000 --- a/.prettierrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "arrowParens": "always", - "bracketSpacing": true, - "embeddedLanguageFormatting": "auto", - "htmlWhitespaceSensitivity": "css", - "insertPragma": false, - "jsxBracketSameLine": false, - "jsxSingleQuote": false, - "printWidth": 80, - "proseWrap": "always", - "quoteProps": "as-needed", - "requirePragma": false, - "semi": true, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "all", - "useTabs": false, - "endOfLine": "lf" -} diff --git a/src/App.js b/src/App.js index 3a49c0e..d138b1c 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,24 @@ import React from "react"; +import { Route, Switch } from "react-router"; import Home from "./pages/Home"; +import Episode from "./pages/Episode"; function App() { - return ; + return ( + + } /> + { + const id = renderProps.match.params.id; + + return ; + }} + /> + + ); } export default App; diff --git a/src/api/requests.js b/src/api/requests.js new file mode 100644 index 0000000..47b5631 --- /dev/null +++ b/src/api/requests.js @@ -0,0 +1,25 @@ +import * as routes from "../constants/routes.js"; + +const path = require("path"); + +export async function getEpisodes({ page = 1 }) { + const url = `${path.join(routes.BASE_URL, routes.EPISODE)}?page=${page}`; + const request = await fetch(url); + const data = await request.json(); + + return data; +} + +export async function getEpisode({ episode }) { + const url = path.join(routes.BASE_URL, routes.EPISODE, episode); + const episodeRequest = await fetch(url); + const episodeData = await episodeRequest.json(); + + const charactersRequest = await Promise.all(episodeData.characters.map((characterURL) => fetch(characterURL))); + const charactersData = await Promise.all(charactersRequest.map((characterRequest) => characterRequest.json())); + + return { + episode: episodeData, + characters: charactersData, + }; +} diff --git a/src/constants/routes.js b/src/constants/routes.js index 8c7af62..65c9da2 100644 --- a/src/constants/routes.js +++ b/src/constants/routes.js @@ -1,3 +1,4 @@ +export const BASE_URL = "https://rickandmortyapi.com/api"; export const HOME = "/"; export const EPISODES = "/episodes"; export const EPISODE = "/episode"; diff --git a/src/pages/Episode/Episode.js b/src/pages/Episode/Episode.js index 8255df5..732351d 100644 --- a/src/pages/Episode/Episode.js +++ b/src/pages/Episode/Episode.js @@ -1,42 +1,80 @@ import React, { Component } from "react"; +import { getEpisode } from "../../api/requests"; import Layout from "../../components/Layout"; -// import CharacterCard from "../../components/CharacterCard"; +import CharacterCard from "../../components/CharacterCard"; class Episode extends Component { - constructor(props) { - super(props); - - this.state = {}; - // episode: null, - // characters: [], - // hasLoaded: false, - // hasError: false, - // errorMessage: null, - } - - render() { - return ( - -
-
- {/* {characters.map((character) => ( - - ))} */} -
-
-
- ); - } + constructor(props) { + super(props); + + this.state = { + episode: null, + characters: [], + hasLoaded: false, + hasError: false, + errorMessage: null, + }; + } + + async componentDidMount() { + this.loadEpisode(); + } + + loadEpisode = async () => { + try { + const data = await getEpisode({ + episode: this.props.id, + }); + + this.setState((prevState) => ({ + ...prevState, + hasLoaded: true, + episode: data.episode, + characters: data.characters, + })); + } catch (error) { + this.setState((prevState) => ({ + ...prevState, + hasError: true, + errorMessage: error, + })); + } + }; + + render() { + const { hasLoaded, hasError, episode, characters } = this.state; + + return ( + +
+ {hasLoaded && !hasError && ( + <> +
+

{episode.name}

+
+
+
+
+
+
+ {episode.episode} | {episode.air_date} +
+
+
+
+
+
+ {characters.map((character) => ( + + ))} +
+ + )} +
+
+ ); + } } export default Episode; diff --git a/src/pages/Home/Home.js b/src/pages/Home/Home.js index f86e93c..c2497eb 100644 --- a/src/pages/Home/Home.js +++ b/src/pages/Home/Home.js @@ -1,57 +1,104 @@ import React, { Component } from "react"; +import { getEpisodes } from "../../api/requests"; import Layout from "../../components/Layout"; -// import EpisodeCard from "../../components/EpisodeCard"; +import EpisodeCard from "../../components/EpisodeCard"; class Home extends Component { - constructor(props) { - super(props); - - this.state = {}; - // page: 1, - // paginationInfo: null, - // episodes: [], - // hasLoaded: false, - // hasError: false, - // errorMessage: null, - } - - async componentDidMount() { - // this.loadEpisodes(); - } - - async loadEpisodes() { - console.log(this); - } - - render() { - return ( - -
- {/* {hasLoaded && !hasError && ( -
-

Episodes loaded!

-
- )} */} -
-
-
- {/* {episodes.map((episode) => ( - - ))} */} -
-
-
-
-
- ); - } + constructor(props) { + super(props); + + this.state = { + page: 1, + paginationInfo: null, + episodes: [], + hasLoaded: false, + hasError: false, + errorMessage: null, + }; + } + + async componentDidMount() { + this.loadEpisodes(); + } + + async componentDidUpdate(prevProps, prevState) { + prevState.page !== this.state.page && this.loadEpisodes(); + } + + goNextPage = () => { + this.setState((prevState) => ({ + ...prevState, + page: ++prevState.page, + })); + }; + + goPrevPage = () => { + this.setState((prevState) => ({ + ...prevState, + page: --prevState.page, + })); + }; + + loadEpisodes = async () => { + try { + const data = await getEpisodes({ + page: this.state.page, + }); + + this.setState((prevState) => ({ + ...prevState, + hasLoaded: true, + episodes: data.results, + paginationInfo: data.info, + })); + } catch (error) { + this.setState((prevState) => ({ + ...prevState, + hasError: true, + errorMessage: error, + })); + } + }; + + render() { + const { hasLoaded, hasError, episodes, paginationInfo } = this.state; + + return ( + +
+ {hasLoaded && !hasError && ( + <> +
+

Episodes loaded!

+
+
+
+
+ {episodes.map((episode) => ( + + ))} +
+
+
+ {paginationInfo.prev && ( + + )} + {paginationInfo.next && ( + + )} +
+
+ + )} +
+
+ ); + } } export default Home; From 53dfb522a0ad736701d7f72ebe56d1e5fc05f5e2 Mon Sep 17 00:00:00 2001 From: sanadriu Date: Sun, 3 Oct 2021 18:45:22 +0200 Subject: [PATCH 02/21] Location and Character pages done --- src/App.js | 20 +++++ src/api/requests.js | 28 +++++++ src/components/CharacterCard/CharacterCard.js | 38 +++++---- .../CharacterProfile/CharacterProfile.js | 46 +++++++++++ .../CharacterProfile/CharacterProfile.scss | 10 +++ src/components/CharacterProfile/index.js | 1 + src/components/Footer/Footer.js | 20 ++--- src/components/Layout/Layout.js | 18 ++-- src/components/Layout/Layout.scss | 3 + src/pages/Character/Character.js | 74 +++++++++++++++++ src/pages/Character/index.js | 1 + src/pages/Episode/Episode.js | 6 +- src/pages/Home/Home.js | 2 +- src/pages/Location/Location.js | 82 +++++++++++++++++++ src/pages/Location/index.js | 1 + 15 files changed, 309 insertions(+), 41 deletions(-) create mode 100644 src/components/CharacterProfile/CharacterProfile.js create mode 100644 src/components/CharacterProfile/CharacterProfile.scss create mode 100644 src/components/CharacterProfile/index.js create mode 100644 src/components/Layout/Layout.scss create mode 100644 src/pages/Character/Character.js create mode 100644 src/pages/Character/index.js create mode 100644 src/pages/Location/Location.js create mode 100644 src/pages/Location/index.js diff --git a/src/App.js b/src/App.js index d138b1c..b7e36f8 100644 --- a/src/App.js +++ b/src/App.js @@ -3,6 +3,8 @@ import { Route, Switch } from "react-router"; import Home from "./pages/Home"; import Episode from "./pages/Episode"; +import Character from "./pages/Character"; +import Location from "./pages/Location"; function App() { return ( @@ -17,6 +19,24 @@ function App() { return ; }} /> + { + const id = renderProps.match.params.id; + + return ; + }} + /> + { + const id = renderProps.match.params.id; + + return ; + }} + /> ); } diff --git a/src/api/requests.js b/src/api/requests.js index 47b5631..bde615e 100644 --- a/src/api/requests.js +++ b/src/api/requests.js @@ -23,3 +23,31 @@ export async function getEpisode({ episode }) { characters: charactersData, }; } + +export async function getCharacter({ character }) { + const url = path.join(routes.BASE_URL, routes.CHARACTER, character); + const characterRequest = await fetch(url); + const characterData = await characterRequest.json(); + + const episodesRequest = await Promise.all(characterData.episode.map((episodeURL) => fetch(episodeURL))); + const episodesData = await Promise.all(episodesRequest.map((episodeRequest) => episodeRequest.json())); + + return { + character: characterData, + episodes: episodesData, + }; +} + +export async function getLocation({ location }) { + const url = path.join(routes.BASE_URL, routes.LOCATION, location); + const locationRequest = await fetch(url); + const locationData = await locationRequest.json(); + + const charactersRequest = await Promise.all(locationData.residents.map((characterURL) => fetch(characterURL))); + const charactersData = await Promise.all(charactersRequest.map((characterRequest) => characterRequest.json())); + + return { + location: locationData, + characters: charactersData, + }; +} diff --git a/src/components/CharacterCard/CharacterCard.js b/src/components/CharacterCard/CharacterCard.js index 9e7e4ab..8bdfd75 100644 --- a/src/components/CharacterCard/CharacterCard.js +++ b/src/components/CharacterCard/CharacterCard.js @@ -6,24 +6,26 @@ import "./CharacterCard.scss"; import * as routes from "../../constants/routes"; function CharacterCard({ id, name, image, species, status, origin, location }) { - return ( -
- - -

{name}

- -
- - {origin.name} - -

|

-

{status}

-
-
- ); + return ( +
+ + +

{name}

+ +
+ {origin.url ? ( + + {origin.name} + + ) : ( +

{origin.name}

+ )} + +

|

+

{status}

+
+
+ ); } export default CharacterCard; diff --git a/src/components/CharacterProfile/CharacterProfile.js b/src/components/CharacterProfile/CharacterProfile.js new file mode 100644 index 0000000..4abfac9 --- /dev/null +++ b/src/components/CharacterProfile/CharacterProfile.js @@ -0,0 +1,46 @@ +import "./CharacterProfile.scss"; + +export default function CharacterProfile(props) { + const { + image, + name, + species, + status, + origin: { name: origin }, + location: { name: location }, + } = props; + + return ( +
+
+ +
+
+
+

{name}

+
+
+
+
+
+
+
CHARACTER
+

+ {species} | {status} +

+
+
+
+
+
ORIGIN
+

{origin}

+
+
+
LOCATION
+

{location}

+
+
+
+
+ ); +} diff --git a/src/components/CharacterProfile/CharacterProfile.scss b/src/components/CharacterProfile/CharacterProfile.scss new file mode 100644 index 0000000..af26b83 --- /dev/null +++ b/src/components/CharacterProfile/CharacterProfile.scss @@ -0,0 +1,10 @@ +.CharacterProfile { + &__img { + display: block; + width: 100%; + border-radius: 3px; + height: auto; + object-fit: contain; + margin-bottom: 1.5rem; + } +} diff --git a/src/components/CharacterProfile/index.js b/src/components/CharacterProfile/index.js new file mode 100644 index 0000000..7d56368 --- /dev/null +++ b/src/components/CharacterProfile/index.js @@ -0,0 +1 @@ +export { default } from "./CharacterProfile"; diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js index 8ed7b26..34807d7 100644 --- a/src/components/Footer/Footer.js +++ b/src/components/Footer/Footer.js @@ -1,17 +1,15 @@ import React from "react"; function Footer() { - return ( -
-
-
-

- Assembler School © {new Date().getFullYear()} -

-
-
-
- ); + return ( +
+
+
+

Assembler School © {new Date().getFullYear()}

+
+
+
+ ); } export default Footer; diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js index a8bdcd3..ed49ef8 100644 --- a/src/components/Layout/Layout.js +++ b/src/components/Layout/Layout.js @@ -4,14 +4,18 @@ import AppHeader from "../AppHeader"; import Main from "../Main"; import Footer from "../Footer"; +import "./Layout.scss"; + function Layout({ children }) { - return ( - <> - -
{children}
-