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
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 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?

# Environment variables
.env
.env.local
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.hbs
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false
}
83 changes: 70 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,78 @@
# GlobalWebIndex Engineering Challenge
# 🐱 Cat Gallery App

## Exercise: CatLover
A responsive and interactive React app for cat lovers, built for a take-home assignment. It integrates with [TheCatAPI](https://thecatapi.com/) to display random cat images, breed information, and manage favorite cats using the official API endpoints.

Create a React application for cat lovers which is going to build upon thecatapi.com and will have 3 views.
The **first** view displays a list of 10 random cat images and a button to load more. Clicking on any of those images opens a modal view with the image and the information about the cat’s breed if available. This would be a link to the second view below - the breed detail. The modal should also contain a form to mark the image as your favourite (a part of the third view as well). Make sure you can copy-paste the URL of the modal and send it to your friends - they should see the same image as you can see.
---

The **second** view displays a list of cat breeds. Each breed opens a modal again with a list of cat images of that breed. Each of those images must be a link to the image detail from the previous point.
## 🚀 Features

The **third** view allows you do the following things:
- 🖼️ View a gallery of 10 random cat images with the ability to "Load More"
- 📌 Click on any image to open a **modal** with detailed info about the image and its breed
- 💾 Mark/unmark an image as **favorite**, using the Cat API’s `/favourites` endpoint
- 🧬 View a list of all **cat breeds** with detail modals and associated breed images
- ❤️ Dedicated **Favorites** page to see, manage, and remove your liked images
- 🔗 Modal URLs are **shareable** – you can copy/paste links directly to friends

- Display your favourite cats
- Remove an image from your favourites (use any UX option you like)
---

You can find the API documentation here: https://developers.thecatapi.com/
We give you a lot of freedom in technologies and ways of doing things. We only insist on you using React.js. Get creative as much as you want, we WILL appreciate it. You will not be evaluated based on how well you follow these instructions, but based on how sensible your solution will be. In case you are not able to implement something you would normally implement for time reasons, make it clear with a comment.
## 🧱 Tech Stack

## Submission
| Tech | Why it's used |
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| [Vite](https://vitejs.dev/) | Fast local development |
| [React](https://reactjs.org/) | Component-based UI |
| [TypeScript](https://www.typescriptlang.org/) | Type safety |
| [React Router](https://reactrouter.com/) | Declarative routing and modals via routes |
| [TanStack Query v5](https://tanstack.com/query/latest) | Data fetching, caching, infinite scrolling |
| [Tailwind CSS v4](https://tailwindcss.com/) | Utility-first styling |
| [Vitest](https://vitest.dev/) + [RTL](https://testing-library.com/docs/react-testing-library/intro/) | Unit/integration testing |

Once you have built your app, share your code in the mean suits you best
Good luck, potential colleague!
---

## 🧪 Testing

Tests are written with Vitest and React Testing Library

```bash
npm run test
```

## 🔧 Setup Instructions

### Install dependencies

```bash
npm install
```

### Run locally

```bash
npm run dev
```

### Run tests

```bash
npm run test
```

### API Integration Notes

#### ⚠️ **API Key Handling Warning**

This application currently stores the API key in the frontend (via `.env` file), which is **not secure** for production applications. Client-side API keys can be easily extracted from browser code.

## 🧠 Design Decisions

- No global state manager (Zustand/Recoil/etc.) — all state is local or in React Query cache

- React Query is used to abstract fetch logic, handle infinite scroll, and cache favorites

- No UI framework (MUI, Radix) — minimal custom components were used to keep things lean and accessible

- Testing during development, not after, to ensure better component isolation and confidence

```

```
28 changes: 28 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
13 changes: 13 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading