diff --git a/README.md b/README.md
index 3c4c4ff..7112c3c 100644
--- a/README.md
+++ b/README.md
@@ -30,9 +30,9 @@ sudo rsync -av --delete dist/ /var/www/html/project1/
echo "Deploy ferdig!"
```
-
## Prettier use
+
```
npm install
-npm run format
-```
\ No newline at end of file
+npm run format
+```
diff --git a/src/Api.tsx b/src/Api.tsx
index fe0b53f..3088be9 100644
--- a/src/Api.tsx
+++ b/src/Api.tsx
@@ -51,3 +51,58 @@ function idFromUrl(url: string): number {
const match = url.match(/\/pokemon\/(\d+)\//);
return match ? Number(match[1]) : 0;
}
+
+/*
+ Type for displayed Pokemon details.
+*/
+export type PokemonDetails = {
+ id: number;
+ name: string;
+ generation: string;
+ types: string[];
+ stats: {
+ name: string;
+ value: number;
+ }[];
+ abilities: string[];
+ imageUrl: string;
+};
+
+/*
+ This function returns the relevant details for a specific Pokemon.
+ Must return a Promise that resolves as a PokemonDetails object.
+*/
+export async function getPokemonDetails(id: number): Promise {
+ const pokemonResponse = await fetch(pokemonApiUrl(id));
+
+ if (!pokemonResponse.ok) {
+ throw new Error(`PokéAPI svarte med ${pokemonResponse.status}`);
+ }
+
+ const data = await pokemonResponse.json();
+
+ const speciesResponse = await fetch(data.species.url);
+
+ if (!speciesResponse.ok) {
+ throw new Error(`Kunne ikke hente species-data`);
+ }
+
+ const speciesData = await speciesResponse.json();
+
+ return {
+ id: data.id,
+ name: data.name,
+ generation: speciesData.generation.name,
+ types: data.types.map((type: { type: { name: string } }) => type.type.name),
+ stats: data.stats.map(
+ (stat: { stat: { name: string }; base_stat: number }) => ({
+ name: stat.stat.name,
+ value: stat.base_stat,
+ })
+ ),
+ abilities: data.abilities.map(
+ (ability: { ability: { name: string } }) => ability.ability.name
+ ),
+ imageUrl: data.sprites.other['official-artwork'].front_default,
+ };
+}
diff --git a/src/App.css b/src/App.css
index 5b47a50..7913902 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,3 +1,14 @@
+/*
+ Used for search results in the pokedex search page.
+*/
+.search-result {
+ background: none;
+ border: none;
+ padding: 0;
+ font: inherit;
+ cursor: pointer;
+}
+
.search-page {
display: flex;
flex-direction: column;
@@ -363,3 +374,160 @@
padding-bottom: 4px;
}
}
+
+/*
+ CSS for pokemon detail card.
+*/
+.pokemon-card {
+ position: relative;
+ width: min(100%, 620px);
+ margin: 24px auto;
+ padding: 20px;
+ box-sizing: border-box;
+
+ border: 1px solid var(--accent-border);
+ border-radius: 18px;
+ background: var(--bg-soft);
+ text-align: left;
+ box-shadow: var(--shadow);
+}
+
+.pokemon-card-close {
+ position: absolute;
+ top: 10px;
+ right: 12px;
+
+ border: 0;
+ background: transparent;
+ color: var(--text);
+ font-size: 22px;
+ cursor: pointer;
+}
+
+.pokemon-card-header {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding-right: 24px;
+}
+
+.pokemon-card-header img {
+ width: 100px;
+ height: 100px;
+ object-fit: contain;
+}
+
+.pokemon-card h2 {
+ margin: 0;
+ font-size: 24px;
+}
+
+.pokemon-card h2 span {
+ color: var(--text);
+ font-size: 16px;
+ font-weight: 400;
+}
+
+.pokemon-generation {
+ margin-top: 4px;
+ color: var(--text);
+ font-size: 14px;
+}
+
+
+
+.pokemon-info {
+ width: 100%;
+ margin-top: 16px;
+ border-spacing: 6px 0;
+ table-layout: fixed;
+}
+
+.pokemon-info th,
+.pokemon-info td {
+ padding: 10px 12px;
+ text-align: left;
+}
+
+.pokemon-info th {
+ color: var(--text-h);
+ font-size: 14px;
+ font-weight: 700;
+}
+
+.pokemon-info td {
+ padding: 10px 12px;
+ border-radius: 10px;
+ background: var(--bg);
+}
+
+.pokemon-stats h3 {
+ color: var(--text-h);
+ font-size: 14px;
+}
+
+.pokemon-info span {
+ color: var(--text);
+ font-size: 14px;
+}
+
+.pokemon-stats {
+ margin-top: 16px;
+}
+
+.pokemon-stats h3 {
+ margin: 0 0 8px;
+}
+
+.stats-grid {
+ width: 100%;
+ border-spacing: 6px;
+ table-layout: fixed;
+}
+
+.stat {
+ padding: 7px 9px;
+ border-radius: 8px;
+ background: var(--bg);
+ font-size: 13px;
+}
+
+.stat span,
+.stat strong {
+ display: inline-block;
+}
+
+.stat strong {
+ float: right;
+}
+
+.stat span {
+ color: var(--text);
+}
+
+.stat strong {
+ color: var(--text-h);
+}
+
+@media (max-width: 600px) {
+ .pokemon-card {
+ padding: 16px;
+ }
+
+ .pokemon-card-header img {
+ width: 80px;
+ height: 80px;
+ }
+
+ .pokemon-card h2 {
+ font-size: 20px;
+ }
+
+ .pokemon-info {
+ border-spacing: 4px 0;
+ }
+
+ .stats-grid {
+ border-spacing: 4px;
+ }
+}
diff --git a/src/App.tsx b/src/App.tsx
index 00cac48..ad25df3 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,9 +1,11 @@
import { useState } from 'react';
import type { FormEvent } from 'react';
import { useQuery } from '@tanstack/react-query';
-import { listAllPokemon } from './Api';
import { useFavorites } from './useFavorites';
import FavoritesSidebar from './FavoritesSidebar';
+import { listAllPokemon } from './Api';
+import PokemonDetails from './PokemonDetails';
+
import './App.css';
const SEARCH_STORAGE_KEY = 'pokemon-search';
@@ -26,8 +28,9 @@ function capitalize(text: string): string {
function App() {
const [query, setQuery] = useState(
- () => sessionStorage.getItem(SEARCH_STORAGE_KEY) ?? '',
+ () => sessionStorage.getItem(SEARCH_STORAGE_KEY) ?? ''
);
+ const [selectedPokemonId, setSelectedPokemonId] = useState(1);
// TanStack Query håndterer henting og caching av Pokémon-lista.
const {
@@ -41,7 +44,7 @@ function App() {
});
const [submittedQuery, setSubmittedQuery] = useState(
- () => sessionStorage.getItem(SEARCH_STORAGE_KEY) ?? '',
+ () => sessionStorage.getItem(SEARCH_STORAGE_KEY) ?? ''
);
const [error, setError] = useState('');
@@ -52,24 +55,22 @@ function App() {
const pokemon = submittedQuery
? allPokemon
- .filter((entry) =>
- entry.name.includes(submittedQuery.toLowerCase()),
- )
- .slice(0, 24)
+ .filter((entry) => entry.name.includes(submittedQuery.toLowerCase()))
+ .slice(0, 24)
: [];
function addToRecentSearches(trimmedQuery: string) {
const withoutDuplicate = recentSearches.filter(
- (entry) => entry.toLowerCase() !== trimmedQuery.toLowerCase(),
+ (entry) => entry.toLowerCase() !== trimmedQuery.toLowerCase()
);
const updated = [trimmedQuery, ...withoutDuplicate].slice(
0,
- MAX_RECENT_SEARCHES,
+ MAX_RECENT_SEARCHES
);
setRecentSearches(updated);
sessionStorage.setItem(
RECENT_SEARCHES_STORAGE_KEY,
- JSON.stringify(updated),
+ JSON.stringify(updated)
);
}
@@ -109,6 +110,14 @@ function App() {
Søk på navn, eller en del av et navn – for eksempel «char» eller
«pikachu».
+
+
+
+
+