Skip to content

Commit

Permalink
Added new UI components, fixed strength and weakness
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianCheung1 committed Oct 5, 2023
1 parent 974ceef commit 91fa6ae
Show file tree
Hide file tree
Showing 14 changed files with 568 additions and 114 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { NextResponse } from "next/server"
import axios from "axios"
import { capitalize } from "@/libs/utils"

export async function GET(
req: Request,
{ params }: { params: { pokemon_name: string } }
) {
const pokemon_name = params.pokemon_name
const pokemon_name = params.pokemon_name.toLowerCase()
try {
const type_weakness_data = await findDualTypeDoubleDmgFrom(pokemon_name)
const type_effectiveness_data = await findDualTypeDoubleDmgTo(pokemon_name)
Expand Down Expand Up @@ -39,15 +40,15 @@ async function getTypeDoubleDmgFrom(typeName: string) {
const response = await axios.get(`https://pokeapi.co/api/v2/type/${typeName}`)
// const data = await response.json()
return response.data.damage_relations.double_damage_from.map(
(type: { name: any }) => type.name
(type: { name: any }) => capitalize(type.name)
)
}

async function getTypeHalfDmgFrom(typeName: string) {
const response = await axios.get(`https://pokeapi.co/api/v2/type/${typeName}`)
// const data = await response.json()
return response.data.damage_relations.half_damage_from.map(
(type: { name: any }) => type.name
(type: { name: any }) => capitalize(type.name)
)
}

Expand Down Expand Up @@ -88,8 +89,9 @@ async function findDualTypeDoubleDmgFrom(pokemonName: string) {
const dualTypeWeakness = [
...doubleDamageFrom1,
...doubleDamageFrom2,
].filter((val) =>
!halfDamageFrom1.includes(val) && !halfDamageFrom2.includes(val)
].filter(
(val) =>
!halfDamageFrom1.includes(val) && !halfDamageFrom2.includes(val)
)

return Array.from(new Set(dualTypeWeakness))
Expand Down Expand Up @@ -122,8 +124,13 @@ async function findDualTypeDoubleDmgTo(pokemonName: string) {
return doubleDmgObject
} else {
const [type] = pokemonTypes
const effectivenessType = await getTypeDoubleDmgTo(type)
return effectivenessType
const doubleDmgTo1 = await getTypeDoubleDmgTo(type)

const doubleDmgObject = {
[type]: doubleDmgTo1,
}

return doubleDmgObject
}
} catch (error) {
return NextResponse.json({ msg: error }, { status: 500 })
Expand Down
18 changes: 18 additions & 0 deletions app/api/pokemons/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import axios from "axios"
import { NextResponse } from "next/server"
export async function GET(req: Request) {
try {
const pokemons = await axios.get(
"https://pogoapi.net//api/v1/released_pokemon.json"
)
return NextResponse.json(
{
msg: "Success",
pokemons: Object.values(pokemons.data),
},
{ status: 200 }
)
} catch (error) {
return NextResponse.json({ msg: error }, { status: 500 })
}
}
32 changes: 20 additions & 12 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import "./globals.css"
import type { Metadata } from "next"
import { MantineProvider, ColorSchemeScript } from "@mantine/core"
import "@mantine/core/styles.css"
import { theme } from "../theme"


const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
title: 'PokeData',
description: 'Pokemon Go Data',
title: "PokeData",
description: "Pokemon Go Data",
}

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
export default function RootLayout({ children }: { children: any }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
<head>
<ColorSchemeScript />
<link rel="shortcut icon" href="/favicon.svg" />
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, user-scalable=no"
/>
</head>
<body>
<MantineProvider theme={theme}>{children}</MantineProvider>
</body>
</html>
)
}
10 changes: 6 additions & 4 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"use client"
import { SearchBar } from "@/components/SearchBar"
import { ColorSchemeToggle } from "@/components/ColorSchemeToggle"
import { Container } from "@mantine/core"

export default function Home() {
return (
<main className="flex flex-col items-center justify-between p-24 w-full h-full">
<SearchBar />
</main>
<Container fluid className="overflow-hidden">
<ColorSchemeToggle />
<SearchBar />
</Container>
)
}
14 changes: 14 additions & 0 deletions components/ColorSchemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use client"

import { Button, Group, useMantineColorScheme } from "@mantine/core"

export function ColorSchemeToggle() {
const { setColorScheme } = useMantineColorScheme()

return (
<Group justify="center" mt="sm">
<Button onClick={() => setColorScheme("light")}>Light</Button>
<Button onClick={() => setColorScheme("dark")}>Dark</Button>
</Group>
)
}
92 changes: 44 additions & 48 deletions components/PokemonCard.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,74 @@
import { capitalize } from "@/libs/utils"
import { Card, Image, Text, Badge, Button, Group } from "@mantine/core"
import { Skeleton } from "@mantine/core"

interface PokemonCardProps {
pokemon: Record<string, any>
isLoading: boolean
}

export const PokemonCard: React.FC<PokemonCardProps> = ({ pokemon }) => {
export const PokemonCard: React.FC<PokemonCardProps> = ({
pokemon,
isLoading,
}) => {
const renderTypeEffectiveness = () => {
const list = []
for (const key in pokemon?.type_effectiveness) {
if (key) list.push(<div>{`${key} Moves`}:</div>)

for (const key in pokemon?.type_effectiveness) {
if (key) list.push(<div>{`${capitalize(key)} Moves`}:</div>)
for (const key2 in pokemon?.type_effectiveness[key]) {
list.push(
<div>{`${pokemon?.type_effectiveness[key][key2]}x more dmg to ${key2}`}</div>
<div>{`Effective against ${pokemon?.type_effectiveness[key][key2]} types`}</div>
)
}
list.push(
<hr className="h-px my-4 bg-gray-200 border-0 dark:bg-gray-700"></hr>
)
}
if (!list) {
return null
if (list.length <= 0) {
return "N/A"
}

return list
}

const renderTypeWeakness = () => {
const list = []
for (const key in pokemon?.type_weakness) {
if (key) list.push(<div>{`${key} Weakness`}:</div>)
return pokemon?.type_weakness?.join(", ")
}

for (const key2 in pokemon?.type_weakness[key]) {
list.push(
<div>{`${pokemon?.type_weakness[key][key2]}x less dmg to ${key2}`}</div>
)
}
const renderBadges = () => {
const list = []
for (const pokemon_types in pokemon?.pokemon_types) {
list.push(
<hr className="h-px my-4 bg-gray-200 border-0 dark:bg-gray-700"></hr>
<Badge color="pink" variant="light">
{pokemon?.pokemon_types[pokemon_types]}
</Badge>
)
}
return pokemon?.type_weakness.join(", ")
if (!list) {
return null
}

return list
}

if (Object.keys(pokemon).length === 0) {
return null
}

return (
<div className="flex w-auto p-8 md:flex-row flex-col rounded-lg justify-center items-center bg-[#3C6E71] shadow-[0_2px_15px_-3px_rgba(0,0,0,0.07),0_10px_20px_-2px_rgba(0,0,0,0.04)]">
<div>
<div className="flex justify-center items-center">
<img className="rounded-lg p-4" src={`${pokemon?.sprite}`} alt="" />
</div>
<div className="flex flex-col">
<div className="flex justify-center">
<h1 className="mb-2 text-2xl font-medium text-[#FFFFFF] dark:text-neutral-50">
{capitalize(pokemon?.pokemon_name)}
</h1>
</div>
<hr className="h-px my-4 bg-gray-200 border-0 dark:bg-gray-700"></hr>
<p className="text-base text-[#D9D9D9] ">
{pokemon?.pokemon_types && `${pokemon?.pokemon_types?.join(", ")}`}
<hr className="h-px my-4 bg-gray-200 border-0 dark:bg-gray-700"></hr>
{renderTypeEffectiveness()}
<Skeleton visible={isLoading}>
<Card shadow="sm" padding="lg" radius="md" withBorder>
<Card.Section>
<Image src={pokemon?.sprite ? `${pokemon?.sprite}` : `/images/og-default-image.jpeg`} height={160} alt="Pokemon" />
</Card.Section>
<Group justify="space-between" mt="md" mb="xs">
<Text size="xl" fw={500}>
{capitalize(pokemon?.pokemon_name)}
</Text>
<Text>{renderBadges()}</Text>
</Group>
<hr className="h-px my-4 bg-gray-200 border-0 dark:bg-gray-700"></hr>
Strengths
<Text size="md" c="dimmed">
{renderTypeEffectiveness()}
</Text>
<hr className="h-px my-4 bg-gray-200 border-0 dark:bg-gray-700"></hr>
<Card.Section inheritPadding mt="sm" pb="md">
Weakness
<Text size="md" c="dimmed">
{renderTypeWeakness()}
</p>
</div>
</div>
</div>
</Text>
</Card.Section>
</Card>
</Skeleton>
)
}
57 changes: 30 additions & 27 deletions components/searchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,45 @@
"use client"
import { useState } from "react"
import usePokemon from "@/hooks/usePokemon"
import { PokemonCard } from "./PokemonCard"
import { BiSearchAlt } from "react-icons/bi"
import { Autocomplete } from "@mantine/core"
import useAllPokemons from "@/hooks/useAllPokemons"
import { Container, Grid, Stack } from "@mantine/core"

export const SearchBar = () => {
const [searchTerm, setSearchTerm] = useState("")
const [submittedTerm, setSubmittedTerm] = useState("")
const { data: pokemon = [], isLoading } = usePokemon(submittedTerm)
const { data: allPokemons = [], isLoading: isPokemonsLoading } =
useAllPokemons()

const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
const handleSubmit = () => {
setSubmittedTerm(searchTerm)
}

return (
<div className="container flex flex-col items-center justify-center">
<form onSubmit={handleSubmit}>
<div className="mb-12 justify-center text-center">
<input
type="text"
value={searchTerm}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value)
setSubmittedTerm("")
}}
className="w-full h-10 px-4 pr-10 text-sm bg-[#3C6E71] border border-[#284B63] rounded-lg lg:w-80 outline-none focus:outline-none text-[#FFFFFF]"
placeholder="Search term..."
/>
<div>
<button type="submit" className="text-[#D9D9D9]">
search
</button>
</div>
</div>
</form>
<div className="mb-12 w-full flex justify-center items-center">
<PokemonCard pokemon={pokemon} />
</div>
</div>
<Container
size="responsive"
className="mt-5 flex items-center justify-center "
>
<Stack className="w-56 md:w-96 ">
<Autocomplete
placeholder="Search term..."
data={
isPokemonsLoading
? []
: allPokemons.pokemons.map((pokemon: { name: any }) => pokemon.name)
}
limit={5}
onChange={(value) => {
setSearchTerm(value)
}}
onOptionSubmit={(value) => {
setSubmittedTerm(value)
}}
/>
<PokemonCard pokemon={pokemon} isLoading={isLoading}/>
</Stack>
</Container>
)
}
16 changes: 16 additions & 0 deletions hooks/useAllPokemons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import useSwr from 'swr'
import fetcher from '@/libs/fetcher'

const useAllPokemons = () => {
const {data, error, isLoading} = useSwr(`/api/pokemons/`, fetcher, {
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false
})

return {
data, error, isLoading
}
}

export default useAllPokemons
6 changes: 3 additions & 3 deletions hooks/usePokemon.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import useSwr from 'swr'
import fetcher from '@/libs/fetcher'

const useTypes = (pokemon_name: string) => {
const {data, error, isLoading} = useSwr(`/api/pokemon/${pokemon_name}`, fetcher, {
const usePokemon = (pokemon_name: string) => {
const {data, error, isLoading} = useSwr(`/api/pokemons/pokemon/${pokemon_name}`, fetcher, {
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false
Expand All @@ -13,4 +13,4 @@ const useTypes = (pokemon_name: string) => {
}
}

export default useTypes
export default usePokemon
Loading

0 comments on commit 91fa6ae

Please sign in to comment.