diff --git a/README.md b/README.md index 11c85e9..57d5a3d 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,111 @@ -# Project 1T30-Project-1 +# Prosjekt 1 - Pokédex (T30) + +A small Pokédex built with React, TypeScript and Vite for the course IT2810 at NTNU. The app fetches pokemon on the fly from [PokeAPI](https://pokeapi.co) and lets you browse them one at a time, filter and sort the selection, and mark favorites that are remembered between visits. ## Links -Repo: [https://git.ntnu.no/IT2810-H26/T30-Project-1] +- Repo: +- Website: (needs NTNU network or VPN) -Webiste: [http://it2810-30.idi.ntnu.no/project1] +## Features +- View one pokemon at a time with sprite, types, height, weight, abilities and base stats +- Step back and forth with Prev/Next, type an id directly, or click a pokemon in the list to jump to it +- Filter by type, sort by id ascending or descending, or show favorites only. The choices survive a reload +- Mark a pokemon as favorite with the heart button. Favorites survive closing the browser and are listed in their own panel +- Responsive layout for desktop, phones in portrait and phones in landscape -## Docs +## Getting started + +Requirements: Node.js 24.6 or newer and npm 11 or newer (see `engines` in `package.json`). + +```bash +git clone https://git.ntnu.no/IT2810-H26/T30-Project-1.git +cd T30-Project-1 +npm install +npm run dev +``` + +Open the localhost address printed in the terminal. Other useful commands: + +| Command | What it does | +| ------------------- | ------------------------------------ | +| `npm run dev` | Start the dev server with hot reload | +| `npm run build` | Type check and build to `dist/` | +| `npm run preview` | Serve the last build locally | +| `npm run test` | Run the Vitest test suite | +| `npm run lint-full` | Lint the whole project with ESLint | +| `npm run format` | Format the project with Prettier | + +Prettier and ESLint also run automatically on staged files when you commit (husky + lint-staged). + +## How to use the app + +1. The card in the middle shows the current pokemon. Use **Prev** and **Next** to move one step, or type a number in the id field to jump straight to that pokemon. +2. The list below the card shows the pokemon around the current one. Click any of them to select it. +3. Open the **Filter** panel to pick a type, change the sort order or show only favorites. Prev/Next and the list then only move between pokemon that match. **Reset** clears the filters. +4. Click the heart to add or remove the current pokemon from your favorites. The favorites panel lists them and you can click one to jump to it. + +## Project structure + +``` +src/ + api/ fetch functions for PokeAPI + models/ filters and favorites, read/write to web storage + controllers/ filtering, navigation and the React hooks + components/ one folder per component with its css and tests + utils/ shared test data and a TanStack Query test wrapper +docs/ architecture diagram, test overview and worksheet +``` + +## Design choices -Read docs in [/docs](/docs/README.md) +- **PokeAPI** (https://pokeapi.co) is the data source. It is open, needs no API key and gives a lot of data per pokemon, so it was easy to build a card, a list and filters on top of it. +- **MVC folder structure.** `src/models/` owns everything that touches web storage, `src/controllers/` owns fetching, filtering and navigation logic (plus the React hooks), and `src/components/` are the views. The views only get props and callbacks, they never read storage or call the API themselves. +- **All requests go through TanStack Query.** The pokemon card is fetched with `useQuery` keyed on the pokemon id, so going back and forth between pokemon you already looked at does not hit the API again. Favorites are fetched the same way keyed on the list of favorite ids. +- **Filtered navigation asks the type endpoint first.** When a type filter is active we fetch `/type/{name}` once to get the ids of that type and only step through those, instead of fetching pokemon one by one until one matches. This keeps the number of API calls down. +- **Two kinds of web storage.** Favorites are stored in `localStorage` so they survive closing the browser. Filter and sort choices are stored in `sessionStorage` so they survive a reload but reset when the tab is closed. Both are validated when read, so broken or old data just falls back to defaults instead of crashing the app. +- **Sorting is by id only.** Sorting by name would need every pokemon fetched up front to know the names, which goes against fetching data on the fly, so we left it out. +- **Plain CSS and own components.** No UI library. Filters use `fieldset`/`legend` with labelled inputs, the favorite and list buttons use `aria-pressed`, loading and error messages use `role="status"` and `role="alert"`, and media queries adapt the layout to narrow screens and to phones in landscape. -## Useful commands +## Testing -> These are generated in ./package.json `scripts` tag. Leaving them here as a cheatsheet. +### Automated -- `npm run dev` - Runs the project in memory, view it from localhost address in stdout -- `npm run lint-full` - Scans project for syntax errors, potential bugs, or stylistic inconsistencies. -- `npm run build` - Compiles TS to JS, remove dead code, bundle source files, etc. Moves the compiled resources to ./dist/ -- `npm run preview` - Runs the previous build to let you test the build before pushing it to web-server or CDN. +Tests are written with Vitest, jsdom and Testing Library. Every component, hook, model and the API module has its own test file, and most components also have a snapshot test. The API is mocked with `vi.mock`/`vi.spyOn` so no test ever calls PokeAPI. Run them with: -> The previous commands we're generated automatically when project was created. What follows is custom behaviour. +```bash +npm run test +``` + +### Manual + +The app was tested manually on the devices and browsers the group had available: + +| Device | Browser | +| ------ | ------- | +| Mac | Safari | +| Mac | Chrome | +| iPhone | Safari | +| iPhone | Chrome | + +## Deployment + +The app is served from Apache on the group's virtual machine under `/project1`. `base` in `vite.config.ts` points there so the built asset paths are correct. To deploy: + +```bash +npm run build +``` + +Then copy the contents of `dist/` to the `project1` directory in Apache's document root on the VM. + +## Use of AI + +Cursor's autofill was used a bit while coding, while ChatGPT was used to go back and forth on ideas and choices. + +## Docs -- `npm run test` - Runs vitest test sets. -- `npm run format` - -Runs prettier to format project (also runs on `git commit`) -- `npm run lint` - Lints only staged changes (`git status`) +More details, the architecture diagram and the worksheet are in [/docs](/docs/README.md). ## Dependencies