Skip to content

Commit

Permalink
Added details on list, search input
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Pacurar committed Oct 3, 2021
1 parent 9015d9b commit 32d3f2e
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 94 deletions.
31 changes: 28 additions & 3 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ app.get("/api/generation", async function (req, res) {
res.json(data.results);
});

const getPokemonIdFromUrl = (url) => {
const urlParts = url.split("/");
return urlParts.slice(-2)[0];
};

app.get("/api/pokemon", async function (req, res) {
const { query } = req;
const { generation, name, type } = query;
const { generation, name } = query;
if (name) {
console.log("poke with name: ", name);
P.getPokemonByName(req.params.id, function (response, error) {
Expand All @@ -54,9 +59,29 @@ app.get("/api/pokemon", async function (req, res) {
const response = await fetch(
`https://pokeapi.co/api/v2/generation/${generation}`,
);

const data = await response.json();
const selectedData = generation === "all" ? data.pokemon_species : data;
res.json(selectedData);
if (generation === "all") {
res.json(data);
} else {
const { pokemon_species } = data;
const result = await Promise.allSettled(
pokemon_species.map(
({ name }) =>
new Promise((resolve, reject) => {
P.getPokemonByName(name, function (response, error) {
if (!error) {
resolve(response);
} else {
reject(error);
}
});
}),
),
);

res.json(result);
}
}
});

Expand Down
4 changes: 0 additions & 4 deletions app/src/App.css

This file was deleted.

11 changes: 0 additions & 11 deletions app/src/App.js

This file was deleted.

8 changes: 7 additions & 1 deletion app/src/components/header/header.container.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import withHeader from "./header";
import { authLogin } from "@Actions/auth-actions";

import { connect } from "react-redux";

Expand All @@ -10,6 +11,11 @@ const mapStateToProps = (state) => {
return { username, token };
};

const wrapper = (...args) => connect(mapStateToProps)(withHeader(...args));
const mapDispatchToProps = (dispatch) => ({
authLogin: (payload) => dispatch(authLogin(payload)),
});

const wrapper = (...args) =>
connect(mapStateToProps, mapDispatchToProps)(withHeader(...args));

export default wrapper;
12 changes: 11 additions & 1 deletion app/src/components/header/header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,20 @@ const Header = ({ history, username }) => (

const withHeader =
(Component) =>
({ username, token }) => {
({ authLogin, username, token }) => {
const location = useLocation();
const history = useHistory();

if (!token) {
const storedToken = localStorage.getItem("token");
if (storedToken) {
authLogin({
username: localStorage.getItem("username"),
token: storedToken,
});
}
}

return token ? (
<>
<Header username={username} history={history} />
Expand Down
1 change: 1 addition & 0 deletions app/src/components/login/login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const Login = ({ authLogin, authLogout }) => {
const { username, token } = data;
authLogin({ username, token });
localStorage.setItem("token", token);
localStorage.setItem("username", username);
const { state } = location;
if (state?.returnToPath) {
history.push(state.returnToPath);
Expand Down
80 changes: 38 additions & 42 deletions app/src/components/pokemon-list/pokemon-list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ const generationsStyle = css`
font-size: 20px;
`;

const searchStyle = css`
text-align: start;
margin: 10px;
`;

const pokemonListStyle = css`
display: flex;
flex-wrap: wrap;
Expand All @@ -34,30 +39,8 @@ const pokemonStyle = css`
border: 1px solid grey;
`;

const getPokemonIdFromUrl = (url) => {
const urlParts = url.split("/");
return urlParts.slice(-2)[0];
};

const PokemonDetails = ({ name, url }) => {
const [details, setDetails] = useState({});
useEffect(() => {
(async () => {
const response = await fetch(
url, // fetch pokemon details
{
headers: {
Accept: "application/json",
},
},
);

const data = await response.json();
setDetails(data);
})();
}, [url]);

const color = details?.color?.name;
const PokemonDetails = ({ details }) => {
const { color, name } = details;

return (
<div css={pokemonStyle} style={{ background: color }}>
Expand All @@ -66,13 +49,12 @@ const PokemonDetails = ({ name, url }) => {
);
};

const BATCH_SIZE = 6;

const PokemonList = () => {
const [characters, setCharacters] = useState([]);
const [generations, setGenerations] = useState([]);
const [selectedGeneration, setSelectedGeneration] = useState(null);
const [maxDisplayed, setMaxDisplayed] = useState(BATCH_SIZE);
const [loadingData, setLoadingData] = useState(false);
const [filterString, setFilterString] = useState("");

useEffect(() => {
(async () => {
Expand All @@ -90,6 +72,9 @@ const PokemonList = () => {
}, []);

useEffect(() => {
if (selectedGeneration) {
setLoadingData(true);
}
(async () => {
if (selectedGeneration) {
const response = await fetch(
Expand All @@ -101,15 +86,25 @@ const PokemonList = () => {
},
);
const data = await response.json();
setCharacters(data.pokemon_species);
if (data.some((r) => r.status !== "fulfilled")) {
console.warn("Some pokemons could not be found!");
}
setCharacters(data.filter((p) => p.status === "fulfilled"));
setLoadingData(false);
}
})();
setMaxDisplayed(BATCH_SIZE);
}, [selectedGeneration]);

const onFilterChange = (event) => setFilterString(event.target.value);

const shownCharacters = filterString
? characters.filter(({ value }) => value.name.indexOf(filterString) !== -1)
: characters;

return (
<div css={pageStyle}>
<h2>Available generations</h2>

<div css={generationsStyle}>
{generations.map(({ name }) => (
<div
Expand All @@ -132,23 +127,24 @@ const PokemonList = () => {
))}
</div>

<div css={pokemonListStyle}>
{characters.slice(0, maxDisplayed).map((c) => (
<Link to={`pokemon/${getPokemonIdFromUrl(c.url)}`} key={c.name}>
<PokemonDetails name={c.name} url={c.url} />
</Link>
))}
<div css={searchStyle}>
Search by:{" "}
<input type="text" value={filterString} onChange={onFilterChange} />
</div>
{maxDisplayed < characters.length && (

{loadingData && (
<div>
<button
type="button"
onClick={() => setMaxDisplayed(maxDisplayed + BATCH_SIZE)}
>
Load more
</button>
<h3>Loading data...</h3>
</div>
)}

<div css={pokemonListStyle}>
{shownCharacters.map(({ value }) => (
<Link to={`pokemon/${value.id}`} key={value.name}>
<PokemonDetails details={value} />
</Link>
))}
</div>
</div>
);
};
Expand Down
11 changes: 4 additions & 7 deletions app/src/components/pokemon/pokemon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@ const Pokemon = () => {

useEffect(() => {
(async () => {
const response = await fetch(
`/api/pokemon/${id}`, // fetch all generations
{
headers: {
Accept: "application/json",
},
const response = await fetch(`/api/pokemon/${id}`, {
headers: {
Accept: "application/json",
},
);
});
const data = await response.json();
setPokemonDetails(data);
})();
Expand Down
8 changes: 5 additions & 3 deletions app/src/index.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
body {
background-color: black;
color: cyan;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
38 changes: 16 additions & 22 deletions app/src/index.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,34 @@
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import reportWebVitals from "./reportWebVitals";
import PokemonList from "./components/pokemon-list";
import Pokemon from "./components/pokemon";
import Login from "./components/login";
import reduxStore from "./store";
import { Provider } from "react-redux";

const Routing = () => {
return (
<Router>
<Switch>
<Route exact path="/" component={PokemonList} />
<Route exact path="/pokemon" component={PokemonList} />
<Route exact path="/pokemon/:id" component={Pokemon} />
<Route exact path="/login" component={Login} />
</Switch>
</Router>
);
};
const Routing = () => (
<Router>
<Switch>
<Route exact path="/" component={PokemonList} />
<Route exact path="/pokemon" component={PokemonList} />
<Route exact path="/pokemon/:id" component={Pokemon} />
<Route exact path="/login" component={Login} />
</Switch>
</Router>
);

const token = localStorage.getItem("token");
if (token) {
reduxStore.auth = { token, username: localStorage.getItem("username") };
}

ReactDOM.render(
<React.StrictMode>
<Provider store={reduxStore}>
<App>
<Routing />
</App>
<Routing />
</Provider>
</React.StrictMode>,
document.getElementById("root"),
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

0 comments on commit 32d3f2e

Please sign in to comment.