Skip to content

3 pokemon details #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
37 changes: 27 additions & 10 deletions src/Home.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
import "./App.css";
import "../../App.css";
import { useState, useEffect } from "react";
import { NavLink } from "react-router-dom";

function Home() {
const [text, setText] = useState("");
const [isReady, setIsReady] = useState(false);

const InputHandler = (event) => {
setText(event.target.value);
if (event.target.value === "Ready!") {
setIsReady(true);
}
if (event.target.value !== "Ready!"){
setIsReady(false)
}
};

return (
<div className="App">
<header className="App-header">
<img
hidden={!isReady}
src="https://www.freeiconspng.com/uploads/file-pokeball-png-0.png"
className="App-logo"
alt="logo"
style={{ padding: "10px" }}
/>
<NavLink to="/auth">
<img
hidden={!isReady}
src="https://www.freeiconspng.com/uploads/file-pokeball-png-0.png"
className="App-logo"
alt="logo"
style={{ padding: "10px" }}
/>
</NavLink>
<b>
Requirement: Try to show the hidden image and make it clickable that
goes to /pokedex when the input below is "Ready!" remember to hide the
red text away when "Ready!" is in the textbox.
</b>
<p>Are you ready to be a pokemon master?</p>
<input type="text" name="name" />
<span style={{ color: "red" }}>I am not ready yet!</span>
<input type="text" name="name" onChange={InputHandler} value={text} />
{isReady ? (
""
) : (
<span style={{ color: "red" }}>I am not ready yet!</span>
)}
</header>
</div>
);
}

export default Home;

125 changes: 107 additions & 18 deletions src/PokeDex.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,64 @@
import "./App.css";
import { useState, useEffect } from "react";
import ReactLoading from "react-loading";
import axios from "axios";
import Modal from "react-modal";
import './App.css';
import { useState, useEffect } from 'react';
import ReactLoading from 'react-loading';
import axios from 'axios';
import Modal from 'react-modal';

import DetailCard from './components/DetailCard';
import ThumbnailCard from './components/ThumbnailCard';
import { MdNavigateNext, MdNavigateBefore } from 'react-icons/md';

function PokeDex() {
const [pokemons, setPokemons] = useState([]);
const [pokemonDetail, setPokemonDetail] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [pokemons, setPokemons] = useState([]);
const [currentPokemonList, setCurrentPokemonList] = useState([]);
const [pokemonDetail, setPokemonDetail] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [nextLink, setNextLink] = useState('');
const [prevLink, setPrevLink] = useState('');
const [search, setSearch] = useState('');
const [notfound, setNotfound] = useState(false);
const [pokemonApi, setPokemonApi] = useState('https://pokeapi.co/api/v2/pokemon');

useEffect(() => {
axios.get(pokemonApi).then((response) => {
console.log(response);
setPokemons(response.data.results);
setCurrentPokemonList(response.data.results);
setNextLink(response.data.next);
setPrevLink(response.data.previous);
});
setIsLoading(false);
}, [pokemonApi]);

useEffect(() => {
if (search === '') {
setPokemons(currentPokemonList);
setNotfound(false);
} else {
const searchedPokemon = currentPokemonList.filter((value) => {
return value.name.toLowerCase().includes(search.toLowerCase());
});
if (searchedPokemon.length > 0) {
setNotfound(false);
setPokemons(searchedPokemon);
} else {
setNotfound(true);
}
}
}, [search, currentPokemonList]);

const handleChange = (e) => setSearch(e.target.value);

const onClickPokemon = (url) => {
axios.get(url).then((response) => {
console.log(response.data);
setPokemonDetail(response.data);
});
};

const onClickNext = () => setPokemonApi(nextLink);

const onClickPrev = () => setPokemonApi(prevLink);

const customStyles = {
content: {
@@ -31,18 +82,24 @@ function PokeDex() {
<h2>Requirement:</h2>
<ul>
<li>
Call this api:https://pokeapi.co/api/v2/pokemon to get pokedex, and show a list of pokemon name.
Call this api:https://pokeapi.co/api/v2/pokemon to get pokedex,
and show a list of pokemon name.
</li>
<li>Implement React Loading and show it during API call</li>
<li>when hover on the list item , change the item color to yellow.</li>
<li>
when hover on the list item , change the item color to yellow.
</li>
<li>when clicked the list item, show the modal below</li>
<li>
Add a search bar on top of the bar for searching, search will run
on keyup event
</li>
<li>Implement sorting and pagingation</li>
<li>Commit your codes after done</li>
<li>If you do more than expected (E.g redesign the page / create a chat feature at the bottom right). it would be good.</li>
<li>
If you do more than expected (E.g redesign the page / create a
chat feature at the bottom right). it would be good.
</li>
</ul>
</header>
</div>
@@ -56,15 +113,43 @@ function PokeDex() {
<>
<div className="App">
<header className="App-header">
<b>Implement loader here</b>
<ReactLoading type="cylon" color="white" />
</header>
</div>
</>
) : (
<>
<h1>Welcome to pokedex !</h1>
<b>Implement Pokedex list here</b>
</>
<h1>Welcome to pokedex !</h1>
<div className="search-box">
<MdNavigateBefore
style={{ cursor: prevLink ? 'pointer' : 'default' }}
color={prevLink ? 'yellow' : 'grey'}
size={30}
onClick={onClickPrev}
/>
<input
className="search-input"
type="text"
name="search"
placeholder="Search Pokemon"
onKeyUp={handleChange}
/>
<MdNavigateNext
style={{ cursor: nextLink ? 'pointer' : 'default' }}
color={nextLink ? 'yellow' : 'grey'}
size={30}
onClick={onClickNext}
/>
</div>
{notfound && <p className="couldnt-find">Couldn't find the searched pokemon!</p>}
{pokemons && (
<div className="list-container">
{pokemons.map((pokemon, index) => (
<ThumbnailCard key={index} onClick={() => onClickPokemon(pokemon.url)} name={pokemon.name} />
))}
</div>
)}
</>
)}
</header>
{pokemonDetail && (
@@ -76,7 +161,8 @@ function PokeDex() {
}}
style={customStyles}
>
<div>
<DetailCard detail={pokemonDetail} />
{/* <div>
Requirement:
<ul>
<li>show the sprites front_default as the pokemon image</li>
@@ -85,9 +171,12 @@ function PokeDex() {
required in tabular format
</li>
<li>Create a bar chart based on the stats above</li>
<li>Create a buttton to download the information generated in this modal as pdf. (images and chart must be included)</li>
<li>
Create a buttton to download the information generated in this
modal as pdf. (images and chart must be included)
</li>
</ul>
</div>
</div> */}
</Modal>
)}
</div>
84 changes: 84 additions & 0 deletions src/components/DetailCard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
.card-container {
width: 80vw;
height: auto;
}

.pokemon-name {
font-size: xx-large;
font-weight: bold;
line-height: 0%;
text-transform: capitalize;
}

.top-content {
display: grid;
}

.pokemon-img {
width: 100%;
max-height: 200px;
object-fit: cover;
}

table {
width: 100%;
text-align: left;
padding: 5px;
border: 1px aliceblue solid;
}

td, th {
text-transform: capitalize;
}

.table-wrapper {
display: flex;
align-items: center;
}

.barchart-wrapper {
width: 100%;
padding-top: 20px;
margin: 0 auto;
}

.bar-box {
background-color: #302e2e;
width: 150px;
height: 6px;
}

.progress-bar {
background-color: yellow;
height: 6px;
}

button {
padding: 5px;
background-color: yellow;
color: black;
margin-top: 20px;
}

@media only screen and (min-width: 770px) {
.card-container {
width: 600px;
}
.top-content {
grid-template-columns: 50% 50%;
}
.pokemon-img {
width: 80%;
height: auto;
max-height: none;
object-fit: cover;
}

.barchart-wrapper {
padding-top: 0;
}

.bar-box {
width: 200px;
}
}
62 changes: 62 additions & 0 deletions src/components/DetailCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useRef } from "react";
import './DetailCard.css';
import { useReactToPrint } from "react-to-print";

const DetailCard = (props) => {
const printRef = useRef(null); // ref to point when print pdf is triggered
const name = props?.detail?.name;
const image = props?.detail?.sprites?.front_default;
const detailStats = props?.detail?.stats;

const onDownloadPdf = useReactToPrint({
content: () => printRef.current
});

const renderStatsTable = (stats) => (
<div className="table-wrapper">
<table>
<tr>
<th>Name</th>
<th>Base Stats</th>
</tr>
{stats.map((stat, index) => (
<tr key={index}>
<td>{stat.stat.name}</td>
<td>{stat.base_stat}</td>
</tr>
))}
</table>
</div>
);

const renderBarChart = (stats) => (
<div className="barchart-wrapper">
<table>
{stats.map((stat, index) => (
<tr key={index}>
<td>{stat.stat.name}</td>
<td>
<div className="bar-box">
<div className="progress-bar" style={{ width: `${stat?.base_stat}%` }} />
</div>
</td>
</tr>
))}
</table>
</div>
);

return (
<div ref={printRef} className="card-container">
<span className="pokemon-name">{name}</span>
<div className="top-content">
<img className="pokemon-img" src={image} alt={name} />
{renderStatsTable(detailStats)}
</div>
{renderBarChart(detailStats)}
<button onClick={onDownloadPdf}>Download As PDF</button>
</div>
);
};

export default DetailCard;
Loading