├── public
├── images
│ ├── pokeball.png
│ ├── pokenext.png
│ ├── unknown-pokemon.png
│ ├── instagram.svg
│ └── github.svg
└── gifs
│ └── pikachu-suprised.gif
├── jsconfig.json
├── postcss.config.js
├── src
├── hooks
│ ├── capitalize.jsx
│ └── getType.jsx
├── pages
│ ├── _app.jsx
│ ├── _document.jsx
│ ├── about.jsx
│ ├── index.jsx
│ └── pokemon
│ │ └── [pokemonId].jsx
├── components
│ ├── UnkownPokemon.jsx
│ ├── PokemonDetails.jsx
│ └── Navbar.jsx
└── styles
│ └── globals.css
├── next.config.js
├── tailwind.config.js
├── .gitignore
├── package.json
└── README.md
/public/images/pokeball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinLyf/pokenext/HEAD/public/images/pokeball.png
--------------------------------------------------------------------------------
/public/images/pokenext.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinLyf/pokenext/HEAD/public/images/pokenext.png
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/public/gifs/pikachu-suprised.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinLyf/pokenext/HEAD/public/gifs/pikachu-suprised.gif
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/images/unknown-pokemon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinLyf/pokenext/HEAD/public/images/unknown-pokemon.png
--------------------------------------------------------------------------------
/src/hooks/capitalize.jsx:
--------------------------------------------------------------------------------
1 | export default function capitalize(string) {
2 | return string.charAt(0).toUpperCase() + string.slice(1);
3 | }
4 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | images: {
5 | domains: ["raw.githubusercontent.com"],
6 | },
7 | }
8 |
9 | module.exports = nextConfig
10 |
--------------------------------------------------------------------------------
/src/pages/_app.jsx:
--------------------------------------------------------------------------------
1 | import '@/styles/globals.css'
2 | import Navbar from "../components/Navbar";
3 | export default function App({ Component, pageProps }) {
4 | return (
5 | <>
6 |
7 |
8 | >
9 | )
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./app/**/*.{js,ts,jsx,tsx}",
5 | "./pages/**/*.{js,ts,jsx,tsx}",
6 | "./components/**/*.{js,ts,jsx,tsx}",
7 |
8 | // Or if using `src` directory:
9 | "./src/**/*.{js,ts,jsx,tsx}",
10 | ],
11 | theme: {
12 | extend: {},
13 | },
14 | plugins: [],
15 | }
--------------------------------------------------------------------------------
/public/images/instagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/pages/_document.jsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
10 |
11 |
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
--------------------------------------------------------------------------------
/public/images/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pokenext",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@headlessui/react": "^1.7.13",
13 | "@heroicons/react": "^2.0.17",
14 | "next": "13.3.0",
15 | "react": "18.2.0",
16 | "react-dom": "18.2.0",
17 | "react-loader-spinner": "^5.3.4"
18 | },
19 | "devDependencies": {
20 | "autoprefixer": "^10.4.14",
21 | "postcss": "^8.4.21",
22 | "tailwindcss": "^3.3.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/UnkownPokemon.jsx:
--------------------------------------------------------------------------------
1 | export default function UnknownPokemon() {
2 | return (
3 |
4 |
9 |
12 |
13 |
14 |
unknown
15 |
16 |
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/hooks/getType.jsx:
--------------------------------------------------------------------------------
1 | export default function getType(type) {
2 | switch (type) {
3 | case "water":
4 | return "water";
5 | case "ice":
6 | return "ice";
7 | case "fire":
8 | return "fire";
9 | case "electric":
10 | return "electric";
11 | case "grass":
12 | return "grass";
13 | case "poison":
14 | return "poison";
15 | case "rock":
16 | return "rock";
17 | case "ground":
18 | return "ground";
19 | case "steel":
20 | return "steel";
21 | case "fairy":
22 | return "fairy";
23 | case "bug":
24 | return "bug";
25 | case "normal":
26 | return "normal";
27 | case "fighting":
28 | return "fighting";
29 | case "psychic":
30 | return "psychic";
31 | case "ghost":
32 | return "ghost";
33 | case "dark":
34 | return "dark";
35 | case "dragon":
36 | return "dragon";
37 | default:
38 | return "unknown";
39 | }
40 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Pokedex
2 |
3 | 
4 |
5 | ## 💻 Description
6 |
7 | I have created an amazing Pokedex using Next.js and Tailwind CSS technologies, and integrating the powerful PokeAPI to provide real-time information on all Pokemon species.
8 |
9 | ## 📖 About
10 |
11 | Creating this Pokedex was a great way to improve my knowledge of Next.js and Tailwind CSS, as well as learn more about integrating APIs into web applications. By using PokeAPI, I was able to deepen my understanding of how to work with external data sources in web development.
12 |
13 | ## 📌 Technologies Used
14 |
15 | - React, version: 18.2.0
16 | - Next, version: 13.3.0
17 | - Tailwind, version: 3.3.1
18 |
19 | ## ⌨ How to use?
20 |
21 | 1. In this first part you will clone the repository:
22 | ```bash
23 | git clone https://github.com/kevinLyf/pokenext.git
24 | ```
25 | 2. Next you will enter the directory:
26 | ```bash
27 | cd pokenext
28 | ```
29 | 3. The next step is to install the dependencies:
30 | ```bash
31 | npm i
32 | ```
33 |
34 | 4. Run an application for development mode:
35 | ```bash
36 | npm run dev
37 | ```
38 |
39 | ## 🧑🚀🚀 Contribution
40 |
41 | Pull requests are welcome. For major changes, open a competition question first what you would like to change.
42 |
43 | ## 👩💻 Author
44 |
45 | Developed by Kevin Feitosa
46 |
--------------------------------------------------------------------------------
/src/pages/about.jsx:
--------------------------------------------------------------------------------
1 | export default function About() {
2 | return (
3 |
4 |
9 |
10 |
Hello Everyone!
11 |
12 | I created this Pokédex using Next.js and Tailwind CSS. To populate the
13 | Pokédex with data, I used the PokeAPI API. By using the PokeAPI API, I
14 | was able to get all the information I needed for each Pokemon,
15 | including name, type, and stats. This information has been organized
16 | and displayed on the Pokédex page, allowing users to easily navigate
17 | and learn more about each Pokémon. Overall, building a Pokédex using
18 | Next.js and Tailwind CSS was a fun and rewarding project. It allowed
19 | me to showcase my web development skills while satisfying my thirst
20 | for knowledge.😎
21 |
22 |
23 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/PokemonDetails.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import capitalize from "../hooks/capitalize";
3 | import getType from "@/hooks/getType";
4 | import UnknownPokemon from "./UnkownPokemon";
5 | import Link from "next/link";
6 |
7 | function PokemonDetails({ pokemonName, isSearch, setIsSearch }) {
8 | const [pokemonData, setPokemonData] = useState(null);
9 |
10 | useEffect(() => {
11 | if (isSearch) {
12 | fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonName}`)
13 | .then((response) => response.json())
14 | .then((data) => setPokemonData(data))
15 | .catch((error) => console.error(error));
16 |
17 | setIsSearch(false);
18 | }
19 | }, [pokemonName, isSearch]);
20 |
21 | if (!pokemonData) {
22 | return ;
23 | }
24 |
25 | return (
26 |
27 |
30 |
e.target.src = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemonData.id}.png`}
34 | alt={pokemonData.name}
35 | />
36 |
37 |
38 |
{capitalize(pokemonData.name)}
39 |
40 |
41 |
42 |
{pokemonData.id}
43 |
44 |
45 |
50 | {pokemonData.types[0].type.name}
51 |
52 |
53 | {pokemonData.types[1]?.type.name && (
54 |
59 | {pokemonData.types[1].type.name}
60 |
61 | )}
62 |
63 |
64 |
65 |
66 | );
67 | }
68 |
69 | export default PokemonDetails;
70 |
--------------------------------------------------------------------------------
/src/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | .icon-search {
6 | @apply absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none;
7 | }
8 |
9 | .input-search {
10 | @apply block w-full p-4 pl-10 text-sm outline-none text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-gray-400 focus:border-gray-400;
11 | }
12 |
13 | .button-search {
14 | @apply text-white absolute right-2.5 bottom-2.5 bg-red-500 hover:bg-red-600 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-4 py-2 cursor-pointer;
15 | }
16 |
17 | .pokemon-wrapper {
18 | @apply mx-auto max-w-7xl grid place-items-center gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 p-4 sm:px-6 lg:px-8;
19 | }
20 |
21 | .pokemon-card {
22 | @apply w-full h-60 flex items-center justify-center bg-green-400 flex-col p-3 text-white hover:brightness-95 cursor-pointer drop-shadow rounded;
23 | }
24 | .pokemon-card-body {
25 | @apply w-full flex items-center justify-between;
26 | }
27 |
28 | .pokemon-card-id {
29 | @apply text-xl font-medium text-white;
30 | }
31 |
32 | .pokemon-card-wrapper {
33 | @apply flex items-center gap-3;
34 | }
35 | .pokemon-card-type {
36 | @apply truncate max-w-full py-1 px-2 text-lg shadow-sm text-white bg-white brightness-105 rounded;
37 | }
38 |
39 | .div-btn-load {
40 | @apply w-full h-full flex items-center justify-center my-4 px-4;
41 | }
42 |
43 | .btn-load {
44 | @apply w-full md:w-2/6 flex items-center justify-center py-3 sm:py-2 px-4 text-xl text-white font-medium bg-red-500 rounded-sm hover:bg-red-600 whitespace-nowrap transition-all;
45 | }
46 |
47 | .grass {
48 | @apply bg-green-400;
49 | }
50 |
51 | .electric {
52 | @apply bg-yellow-400;
53 | }
54 |
55 | .water {
56 | @apply bg-blue-400;
57 | }
58 |
59 | .ice {
60 | @apply bg-blue-600;
61 | }
62 |
63 | .fire {
64 | @apply bg-red-500;
65 | }
66 |
67 | .rock {
68 | @apply bg-orange-400;
69 | }
70 |
71 | .ground {
72 | @apply bg-orange-600;
73 | }
74 |
75 | .poison {
76 | @apply bg-purple-400;
77 | }
78 |
79 | .fairy {
80 | @apply bg-pink-400;
81 | }
82 |
83 | .bug {
84 | @apply bg-emerald-400;
85 | }
86 |
87 | .normal {
88 | @apply bg-yellow-600;
89 | }
90 |
91 | .fighting {
92 | @apply bg-yellow-700;
93 | }
94 |
95 | .psychic {
96 | @apply bg-pink-500;
97 | }
98 |
99 | .ghost {
100 | @apply bg-gray-500;
101 | }
102 |
103 | .dark {
104 | @apply bg-slate-800;
105 | }
106 |
107 | .dragon {
108 | @apply bg-red-600;
109 | }
110 |
111 | .steel {
112 | @apply bg-slate-400;
113 | }
114 |
115 | .unknown {
116 | @apply bg-zinc-700;
117 | }
118 |
--------------------------------------------------------------------------------
/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import { Disclosure } from "@headlessui/react";
2 | import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
3 | import { useRouter } from "next/router";
4 |
5 | function classNames(...classes) {
6 | return classes.filter(Boolean).join(" ");
7 | }
8 |
9 | export default function Navbar() {
10 | const router = useRouter();
11 |
12 | const navigation = [
13 | { name: "Home", href: "/", current: router.pathname === "/" },
14 | { name: "About", href: "/about", current: router.pathname === "/about" },
15 | ];
16 |
17 | return (
18 |
19 | {({ open }) => (
20 | <>
21 |
22 |
23 |
24 | {/* Mobile menu button*/}
25 |
26 | Open main menu
27 | {open ? (
28 |
29 | ) : (
30 |
31 | )}
32 |
33 |
34 |
35 |
36 |
41 |
46 |
47 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {navigation.map((item) => (
73 |
85 | {item.name}
86 |
87 | ))}
88 |
89 |
90 | >
91 | )}
92 |
93 | );
94 | }
95 |
--------------------------------------------------------------------------------
/src/pages/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import getType from "../hooks/getType";
3 | import capitalize from "../hooks/capitalize";
4 | import PokemonDetails from "../components/PokemonDetails";
5 | import { ThreeDots } from "react-loader-spinner";
6 | import Link from "next/link";
7 |
8 | function App() {
9 | const [pokemons, setPokemons] = useState([]);
10 | const [pokemonAmount, setPokemonAmount] = useState(16);
11 | const [isLoading, setIsLoading] = useState(false);
12 | const [pokemonName, setPokemonName] = useState("");
13 | const [isSearch, setIsSearch] = useState(false);
14 |
15 | const searchHandle = (e) => {
16 | e.preventDefault();
17 | setIsSearch(true);
18 | };
19 |
20 | useEffect(() => {
21 | const fetchPokemons = async () => {
22 | setIsLoading(true);
23 | const response = await fetch(
24 | `https://pokeapi.co/api/v2/pokemon?limit=${pokemonAmount}`
25 | );
26 | const data = await response.json();
27 | const results = data.results;
28 |
29 | const pokemonData = await Promise.all(
30 | results.map(async (pokemon) => {
31 | const response = await fetch(pokemon.url);
32 | const data = await response.json();
33 | const types = data.types.map((type) => type.type.name);
34 | return {
35 | id: data.id,
36 | name: data.name,
37 | types: types,
38 | };
39 | })
40 | );
41 |
42 | setPokemons(pokemonData);
43 | setIsLoading(false);
44 | };
45 |
46 | fetchPokemons();
47 | }, [pokemonAmount]);
48 |
49 |
50 | return (
51 |
52 |
89 |
90 |
91 |
92 | {pokemons.map((pokemon) => (
93 |
98 |
99 |
103 | (e.target.src = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`)
104 | }
105 | alt={pokemon.name}
106 | />
107 |
108 |
109 | {capitalize(pokemon.name)}
110 |
111 |
112 |
113 |
{pokemon.id}
114 |
115 | {pokemon.types.map((type, index) => (
116 |
122 | {type}
123 |
124 | ))}
125 |
126 |
127 |
128 |
129 | ))}
130 |
131 |
132 |
133 |
134 | setPokemonAmount((amount) => amount + 20)}
137 | disabled={isLoading}
138 | >
139 | {isLoading ? (
140 |
150 | ) : (
151 | "Load More"
152 | )}
153 |
154 |
155 |
156 | );
157 | }
158 |
159 | export default App;
160 |
--------------------------------------------------------------------------------
/src/pages/pokemon/[pokemonId].jsx:
--------------------------------------------------------------------------------
1 | import capitalize from "@/hooks/capitalize";
2 | import getType from "@/hooks/getType";
3 |
4 | export async function getStaticPaths() {
5 | const maxPokemons = 1008;
6 |
7 | const res = await fetch(
8 | `https://pokeapi.co/api/v2/pokemon/?limit=${maxPokemons}`
9 | );
10 | const data = await res.json();
11 |
12 | const paths = data.results.map((pokemon, index) => {
13 | return {
14 | params: { pokemonId: (index + 1).toString() },
15 | };
16 | });
17 |
18 | return {
19 | paths,
20 | fallback: false,
21 | };
22 | }
23 |
24 | export async function getStaticProps(context) {
25 | const id = context.params.pokemonId;
26 |
27 | const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
28 | const data = await res.json();
29 |
30 | const abilities = await Promise.all(
31 | data.abilities.map(async (ability) => {
32 | const response = await fetch(
33 | `https://pokeapi.co/api/v2/ability/${ability.ability.name}`
34 | );
35 | const info = await response.json();
36 | return { name: ability.ability.name, info };
37 | })
38 | );
39 |
40 | return {
41 | props: { pokemon: data, abilities },
42 | };
43 | }
44 |
45 | export default function PokemonId({ pokemon, abilities }) {
46 |
47 | return (
48 |
49 |
50 |
51 |
55 | (e.target.src = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`)
56 | }
57 | alt={pokemon.name}
58 | />
59 |
60 |
61 | {capitalize(pokemon.name)}
62 |
63 |
64 |
65 |
{pokemon.id}
66 |
67 |
68 |
73 | {pokemon.types[0].type.name}
74 |
75 |
76 | {pokemon.types[1]?.type.name && (
77 |
82 | {pokemon.types[1].type.name}
83 |
84 | )}
85 |
86 |
87 |
88 |
89 |
90 |
91 |
Status
92 |
93 |
94 |
95 |
96 |
HP
97 |
98 | {pokemon.stats[0].base_stat}
99 |
100 |
101 |
102 |
113 |
114 |
115 |
116 |
117 |
118 | Attack
119 |
120 |
121 | {pokemon.stats[1].base_stat}
122 |
123 |
124 |
125 |
136 |
137 |
138 |
139 |
140 |
141 | Defense
142 |
143 |
144 | {pokemon.stats[2].base_stat}
145 |
146 |
147 |
148 |
159 |
160 |
161 |
162 |
163 |
164 | Speed
165 |
166 |
167 | {pokemon.stats[5].base_stat}
168 |
169 |
170 |
171 |
182 |
183 |
184 |
185 |
186 |
187 | Special Attack
188 |
189 |
190 | {pokemon.stats[3].base_stat}
191 |
192 |
193 |
194 |
205 |
206 |
207 |
208 |
209 |
210 | Special Defense
211 |
212 |
213 | {pokemon.stats[4].base_stat}
214 |
215 |
216 |
217 |
228 |
229 |
230 |
231 |
232 |
233 |
Abilities
234 |
235 |
236 | {abilities.map((ability) => (
237 |
241 |
242 |
{capitalize(ability.name)}
243 |
244 |
245 | {ability.info.effect_entries
246 | .filter((effect) => effect.language.name.includes("en"))
247 | .map((effect, index) => (
248 |
249 | {effect.effect}
250 |
251 | ))}
252 |
253 | ))}
254 |
255 |
256 |
257 |
258 | );
259 | }
260 |
--------------------------------------------------------------------------------