Skip to content
Open
Show file tree
Hide file tree
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
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"semi": true,
"tabWidth": 2,
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all",
"jsxSingleQuote": true,
"bracketSpacing": true
}
151 changes: 101 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,103 @@
# Looqbox FrontEnd Challenge

### Would you like to work with us? Apply [here](https://looqbox.gupy.io/)!

# Looqbox FrontEnd Challenge
![Looqbox](https://github.com/looqbox/looqbox-frontend-challenge/blob/master/logo.png)

## Challenge
In this challenge you will need to build a **S**ingle **P**age **A**pplication using ReactJS and a provided api

We will not use anything from your project other than evaluate your skills and you are free to use it in your portfolio

## Stack
We use:
- ReactJS
- Redux
- TypeScript
- AntDesign

## Submitting
- Make a fork of this repository
- Create your branch
- ⚠️ Do a initial Commit when you start
- ⚠️ Do a final commit when you finish
- When you're done send us a pull request

# Guidelines
You need to create a Single Page Application (SPA) that displays a list of Pokémon and allows users to search for them, using the [Pokeapi](https://pokeapi.co/docs/v2). Your app must be dynamic, meaning you **must not** reload the page to show new content.

The PokeAPI was chosen for its simplicity in making requests. Since it is an open API, please **be mindful of how many requests** you make.

## Requirements:

- On the main page, include a search bar and a preloaded list of Pokémon.
- Clicking on any Pokémon should display a card, modal, or page with that Pokémon’s information.
- Typing in the search bar and pressing Enter should display the search result instead of the list.
- Your app must include at least two different routes (e.g., /home, /details — be creative!).
- Add a README file to document your project.

You may use any libraries or dependencies you like (e.g., Axios, Bootstrap, Material UI...).

## Bonus points!
- Pagination
- Error handling
- Documentation
- Linting
- Charts
- Unit Testing
- Ant Design

## Useful links
- [React docs](https://react.dev/)
- [PokeApi docs](https://pokeapi.co/docs/v2)
- [Redux](https://redux.js.org/)
This project is a solution for the FrontEnd technical challenge proposed by Looqbox. It consists of a Single Page Application (SPA) that consumes the PokeAPI to provide a dynamic interface for listing and searching Pokémon.

## Objective

The primary goal of this project is to build a robust application using ReactJS, demonstrating proficiency in state management, routing, and external API consumption. The application allows users to view a preloaded list, search by name or ID, and access detailed information for each Pokémon without reloading the page.

## Project Structure

The project follows a modular architecture to ensure a clear separation of concerns:

```text
.
├── src/
├── assets/ # Media files and icons
├── components/
│ ├── common/ # Shared UI components like Error and Empty states
│ └── pokemon/ # Domain-specific Pokémon components
├── config/ # Theme configurations and Design Tokens
├── context/ # React Contexts for global theme management
├── hooks/ # Custom hooks for Redux and UI logic
├── pages/ # High-level views (Home and Details)
├── routes/ # Routing configuration
├── services/ # API communication layer
├── store/ # Global state management with Redux Toolkit
├── types/ # TypeScript interface and type definitions
└── utils/ # Utility functions for formatting and color mapping

```

## Stack and Libraries

- **ReactJS & TypeScript**
- **Redux Toolkit**
- **Ant Design (antd)**
- **React Router Dom**
- **Recharts**
- **Vite**

## Features

- **Dynamic Search**
- **Pagination**
- **Dark Mode**
- **Error Handling**
- **Data Visualization**

## Missing

- **80% coverage for tests**

## Getting Started

### Prerequisites

- Node.js (version 18 or higher recommended)
- Yarn or NPM

### Installation

1. Clone the repository:

```bash
git clone <repository-url>

```

2. Navigate to the project directory:

```bash
cd looqbox-frontend-challenge

```

3. Install the dependencies:

```bash
yarn install

```

### Running the Project

To start the development server, run:

```bash
yarn dev

```

The application will be available at `http://localhost:5173`.

### Building for Production

To create an optimized production build, run:

```bash
yarn build

```
38 changes: 38 additions & 0 deletions coverage/clover.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<coverage generated="1771469117411" clover="3.2.0">
<project timestamp="1771469117411" name="All files">
<metrics statements="20" coveredstatements="20" conditionals="8" coveredconditionals="8" methods="5" coveredmethods="5" elements="33" coveredelements="33" complexity="0" loc="20" ncloc="20" packages="2" files="2" classes="2"/>
<package name="components.pokemon">
<metrics statements="17" coveredstatements="17" conditionals="8" coveredconditionals="8" methods="3" coveredmethods="3"/>
<file name="PokemonCard.tsx" path="/Users/uszacki/looqbox-challenge/src/components/pokemon/PokemonCard.tsx">
<metrics statements="17" coveredstatements="17" conditionals="8" coveredconditionals="8" methods="3" coveredmethods="3"/>
<line num="2" count="1" type="stmt"/>
<line num="3" count="1" type="stmt"/>
<line num="5" count="1" type="stmt"/>
<line num="6" count="1" type="stmt"/>
<line num="7" count="1" type="stmt"/>
<line num="9" count="1" type="stmt"/>
<line num="15" count="1" type="stmt"/>
<line num="16" count="3" type="stmt"/>
<line num="17" count="3" type="stmt"/>
<line num="19" count="3" type="stmt"/>
<line num="20" count="3" type="stmt"/>
<line num="22" count="3" type="cond" truecount="2" falsecount="0"/>
<line num="25" count="3" type="cond" truecount="2" falsecount="0"/>
<line num="26" count="3" type="cond" truecount="2" falsecount="0"/>
<line num="28" count="3" type="stmt"/>
<line num="42" count="1" type="stmt"/>
<line num="95" count="6" type="stmt"/>
</file>
</package>
<package name="utils">
<metrics statements="3" coveredstatements="3" conditionals="0" coveredconditionals="0" methods="2" coveredmethods="2"/>
<file name="formatName.ts" path="/Users/uszacki/looqbox-challenge/src/utils/formatName.ts">
<metrics statements="3" coveredstatements="3" conditionals="0" coveredconditionals="0" methods="2" coveredmethods="2"/>
<line num="1" count="1" type="stmt"/>
<line num="2" count="7" type="stmt"/>
<line num="4" count="14" type="stmt"/>
</file>
</package>
</project>
</coverage>
3 changes: 3 additions & 0 deletions coverage/coverage-final.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{"/Users/uszacki/looqbox-challenge/src/components/pokemon/PokemonCard.tsx": {"path":"/Users/uszacki/looqbox-challenge/src/components/pokemon/PokemonCard.tsx","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":45}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":47}},"2":{"start":{"line":5,"column":0},"end":{"line":5,"column":52}},"3":{"start":{"line":6,"column":0},"end":{"line":6,"column":59}},"4":{"start":{"line":7,"column":0},"end":{"line":7,"column":48}},"5":{"start":{"line":9,"column":24},"end":{"line":9,"column":34}},"6":{"start":{"line":15,"column":55},"end":{"line":112,"column":1}},"7":{"start":{"line":16,"column":19},"end":{"line":16,"column":32}},"8":{"start":{"line":17,"column":25},"end":{"line":17,"column":35}},"9":{"start":{"line":19,"column":19},"end":{"line":19,"column":45}},"10":{"start":{"line":20,"column":21},"end":{"line":20,"column":41}},"11":{"start":{"line":22,"column":24},"end":{"line":24,"column":23}},"12":{"start":{"line":25,"column":23},"end":{"line":25,"column":57}},"13":{"start":{"line":26,"column":27},"end":{"line":26,"column":85}},"14":{"start":{"line":28,"column":2},"end":{"line":111,"column":4}},"15":{"start":{"line":42,"column":21},"end":{"line":42,"column":57}},"16":{"start":{"line":95,"column":12},"end":{"line":96,"column":null}},"17":{"start":{"line":15,"column":13},"end":{"line":15,"column":55}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":15,"column":55},"end":{"line":15,"column":56}},"loc":{"start":{"line":15,"column":71},"end":{"line":112,"column":1}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":42,"column":15},"end":{"line":42,"column":18}},"loc":{"start":{"line":42,"column":21},"end":{"line":42,"column":57}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":94,"column":29},"end":{"line":94,"column":30}},"loc":{"start":{"line":95,"column":12},"end":{"line":96,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":22,"column":24},"end":{"line":24,"column":23}},"type":"cond-expr","locations":[{"start":{"line":23,"column":6},"end":{"line":23,"column":66}},{"start":{"line":24,"column":6},"end":{"line":24,"column":23}}]},"1":{"loc":{"start":{"line":25,"column":23},"end":{"line":25,"column":57}},"type":"cond-expr","locations":[{"start":{"line":25,"column":36},"end":{"line":25,"column":45}},{"start":{"line":25,"column":48},"end":{"line":25,"column":57}}]},"2":{"loc":{"start":{"line":26,"column":27},"end":{"line":26,"column":85}},"type":"cond-expr","locations":[{"start":{"line":26,"column":40},"end":{"line":26,"column":64}},{"start":{"line":26,"column":67},"end":{"line":26,"column":85}}]},"3":{"loc":{"start":{"line":57,"column":21},"end":{"line":57,"column":79}},"type":"cond-expr","locations":[{"start":{"line":57,"column":34},"end":{"line":57,"column":58}},{"start":{"line":57,"column":61},"end":{"line":57,"column":79}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":3,"8":3,"9":3,"10":3,"11":3,"12":3,"13":3,"14":3,"15":1,"16":6,"17":1},"f":{"0":3,"1":1,"2":6},"b":{"0":[1,2],"1":[1,2],"2":[1,2],"3":[1,2]}}
,"/Users/uszacki/looqbox-challenge/src/utils/formatName.ts": {"path":"/Users/uszacki/looqbox-challenge/src/utils/formatName.ts","statementMap":{"0":{"start":{"line":1,"column":33},"end":{"line":6,"column":1}},"1":{"start":{"line":2,"column":2},"end":{"line":5,"column":15}},"2":{"start":{"line":4,"column":19},"end":{"line":4,"column":63}},"3":{"start":{"line":1,"column":13},"end":{"line":1,"column":33}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":1,"column":33},"end":{"line":1,"column":34}},"loc":{"start":{"line":1,"column":58},"end":{"line":6,"column":1}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":4,"column":9},"end":{"line":4,"column":10}},"loc":{"start":{"line":4,"column":19},"end":{"line":4,"column":63}}}},"branchMap":{},"s":{"0":1,"1":7,"2":14,"3":1},"f":{"0":7,"1":14},"b":{}}
}
Loading