Skip to content
Merged
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
25 changes: 11 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@ Country Explorer lets you browse European countries, filter by subregion, sort b
name or population, and save favorites. Built with React, TypeScript and Vite by
team IT2810-H26-T23.

## Requirements

- Git and access to this repository.
- Node.js **22.20.0** and npm **10.9.3** (the versions used for local verification).
- Internet access to install dependencies and load country data and flags.

No API key or environment file is needed. Check your installed versions with
`node --version` and `npm --version`.

## Run locally

1. Clone the repository and enter the application folder:
Expand All @@ -27,7 +18,6 @@ No API key or environment file is needed. Check your installed versions with
2. Install the locked dependencies:

```bash
npm install
npm ci
```

Expand Down Expand Up @@ -69,14 +59,21 @@ See the deployment document below for serving the build on the VM.

## Working in this repository

`main` is protected — you cannot push to it directly. Work on a branch and
open a pull request:
Development work is integrated through `dev`. Start each issue branch from an
up-to-date `dev` branch, push the issue branch, and open a pull request into
`dev`:

```bash
git checkout -b my-feature
git push -u origin my-feature
git switch dev
git pull --ff-only
git switch -c "<issue-number>-<short-description>"
git push -u origin "<issue-number>-<short-description>"
```

When the complete project is ready for release, `dev` is merged into `main`.
Both shared branches are protected, so changes reach them through reviewed
merge requests rather than direct pushes.

Before a pull request can be merged:

1. **A member of IT2810-H26-T23 must approve it.** You cannot approve your own pull
Expand Down
112 changes: 25 additions & 87 deletions country-explorer/README.md
Original file line number Diff line number Diff line change
@@ -1,101 +1,39 @@
# Country Explorer
# Country Explorer application

React + TypeScript + Vite app that fetches and displays country data from a REST API.
This directory contains the React, TypeScript, and Vite application. It fetches
European country data from a REST API and lets users filter, sort, navigate, and
save favorite countries.

Project-level installation, testing, deployment, and Git workflow instructions
are in the [main README](../README.md). Details about the data source and mapping
are in the [API documentation](./src/api/README.md).

## Project structure

```
```text
src/
├── api/ # Functions for calling the external REST API (one file per resource)
├── components/ # Reusable UI components, e.g. App.tsx (one file per component)
├── hooks/ # Custom React hooks
├── api/ # REST API client and API documentation
├── components/ # React components, styles, and component tests
├── hooks/ # Custom React and TanStack Query hooks
├── lib/ # Shared library configuration
├── test/ # Shared test setup and fixtures
├── types/ # Shared TypeScript types and interfaces
├── utils/ # Small, framework-agnostic helper functions
├── assets/ # Images, icons, and other static assets
├── utils/ # Storage and sorting helpers
├── assets/ # Images and other bundled assets
├── index.css # Global stylesheet
└── main.tsx # Application entry point
```

`api/`, `hooks/`, `types/`, and `utils/` are created once there is real code to put in them. See [`src/api/README.md`](./src/api/README.md) for which REST API is used, the selected countries, data fields, and error handling.

Naming conventions:

- Components: PascalCase file names matching the default export (e.g. `CountryCard.tsx`), colocated with their own CSS file when they have one (e.g. `App.tsx` + `App.css`).
- Hooks: camelCase file names prefixed with `use` (e.g. `useFetch.ts`).
- API functions & utilities: camelCase file names describing what they do (e.g. `getCountries.ts`, `formatNumber.ts`).
- Types: camelCase file names matching the domain they describe (e.g. `country.ts`).
- Tests are colocated with the file they test (e.g. `CountryCard.test.tsx` next to `CountryCard.tsx`), rather than kept in a separate top-level test folder.

## About this template

This project was scaffolded with the React + TypeScript + Vite template, which provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)

### React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
- Components use PascalCase file names matching their default export and keep
their CSS and tests beside the component.
- Hooks use camelCase names beginning with `use`.
- API functions and utilities use descriptive camelCase names.
- Shared domain types use descriptive camelCase file names.

### Expanding the ESLint configuration
## Tooling

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...

// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,

// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
]);
```

You can also install [eslint-plugin-react-x](https://npmx.dev/package/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://npmx.dev/package/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x';
import reactDom from 'eslint-plugin-react-dom';

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
]);
```
The app uses React, TanStack Query, TypeScript, Vite, ESLint, Prettier, Vitest,
and Testing Library. The React Compiler is not enabled. Vite's base path is
`/project1/`, matching the deployment path on the group VM.
44 changes: 27 additions & 17 deletions country-explorer/src/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

The application uses [countries.dev](https://countries.dev) to fetch country data. It's a free, publicly available API with no API key, sign-up, or authentication required. It's the successor to `apicountries.com`, which now redirects to it, and returns the same field shape as the (now retired) `restcountries.com` v2 API.

## Endpoints used
## Endpoints

- `GET /region/{region}` — fetches every country in a region. Used once to load all European countries: `/region/europe`.
- `GET /alpha/{code}` — fetches a single country by its ISO alpha-2/alpha-3 code. Used when looking up or refreshing one specific country by code.
- `GET /region/{region}` — fetches every country in a region. The application
uses `/region/europe` to load its country list.
- `GET /alpha/{code}` — fetches a single country by its ISO alpha-2 or alpha-3
code. A client function is implemented for this endpoint, but the current UI
does not call it.

## Selected countries

Expand Down Expand Up @@ -67,21 +70,21 @@ All 53 entries returned by `/region/europe`:
| Ukraine | UA | UKR |
| United Kingdom of Great Britain and Northern Ireland | GB | GBR |

This list includes a handful of dependent territories alongside sovereign states (Åland Islands, Faroe Islands, Gibraltar, Guernsey, Isle of Man, Jersey, Svalbard and Jan Mayen), following the API's own region classification. Each country's `independent` field can be used later to filter down to sovereign states only, if that turns out to be desired.
This list includes a handful of dependent territories alongside sovereign states (Åland Islands, Faroe Islands, Gibraltar, Guernsey, Isle of Man, Jersey, Svalbard and Jan Mayen), following the API's own region classification.

## Data fields used

| Field (from the wireframe) | API key(s) | Notes |
| -------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Name | `name` | |
| Flag | `Mapped to flagUrl, `flags.png` | `flag` also gives an emoji flag |
| Capital | `capital` | |
| Region | `region`, `subregion` | Used by `RegionFilter` |
| Population | `population` | |
| Area | `area` | In km² |
| Currency | `currencies` | |
| Languages | `languages` | |
| | `alpha2Code`, `Mapped to code for selection, favorites, and lookups` | Not shown in the UI; used internally as a stable id/key and for `/alpha/{code}` lookups |
| UI/internal field | API key(s) | Notes |
| ----------------- | --------------------- | --------------------------------------------------------- |
| Name | `name` | |
| Flag URL | `flags.svg` | Mapped to `flagUrl` |
| Capital | `capital` | |
| Region/filter | `region`, `subregion` | Subregion is preferred by `RegionFilter` |
| Population | `population` | |
| Area | `area` | Displayed in km² |
| Currencies | `currencies` | Names and codes are combined into display strings |
| Languages | `languages` | Language names become display strings |
| Country code | `alpha3Code` | Mapped to `code`; used for selection, favorites, and keys |

## TypeScript models

Expand Down Expand Up @@ -112,8 +115,15 @@ The API responds with a normal HTTP status code plus a small JSON error body:
| `404` | No match — unknown/typo'd code or name | `{ "error": "Country not found" }` |
| `429` | Abuse ceiling hit (~60 requests/second per IP; there's no API key or per-key quota) | `{ "error": "Too Many Requests", "message": "Rate limit reached" }` |

A `404` is treated as "no result" in the UI (e.g. an empty/error state), not as a crash, since it normally just means the requested code or name didn't match anything. The app only needs one `/region/europe` call plus occasional `/alpha/{code}` lookups, so the `429` ceiling should never realistically be hit in normal use.
Network failures and non-success HTTP responses, including `404`, are converted
to `ApiError` instances. TanStack Query exposes the error to `App`, which displays
an error message and a retry button. A failed request therefore produces a
recoverable error state rather than crashing the application. Normal UI use only
needs the `/region/europe` request, so the `429` ceiling should not be reached.

## Data storage

Fetched country data is only kept in memory (component state / the data-fetching library's cache) and is never written to `localStorage` or `sessionStorage`. Only user-created state — favorites, selected region, sort option — may be persisted to Web Storage (see `wireframes-and-component-structure.md`).
Fetched country data is only kept in the TanStack Query memory cache and is never
written to `localStorage` or `sessionStorage`. Only user-created state — favorites,
selected region, and sorting option — is persisted to Web Storage. See
[`docs/component-structure.md`](../../../docs/component-structure.md).
Loading