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
64 changes: 40 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ The calculator and About pages are placeholders. Adding items from the catalog t

## What to review

| Review | What to look at |
| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| [Running application](http://it2810-29.idi.ntnu.no/project2) | Search, load more, element details, and the Your gymnasts test login |
| [ElementsPage tests](src/components/elements/ElementsPage.test.tsx) | Search, result paging, URL state, loading, and error behavior |
| [Element detail tests](src/components/elements/ElementDetailPage.test.tsx) | Catalog detail rendering and error/loading states |
| [Gymnast page tests](src/components/gymnasts/YourGymnastsPage.test.tsx) | Test login, loading, profile creation, and error handling |
| [Backend catalog tests](backend/src/catalog/search.test.ts) | Search, filters, sorting, pagination, and count limits |
| [Backend server tests](backend/src/server/server.test.ts) | GraphQL API behavior; database-backed tests require a migrated and seeded database |
| [docs/API.md](docs/API.md) | API design, schema, search, pagination, authentication, and errors |
| [docs/AI_USAGE.md](docs/AI_USAGE.md) | How AI was used and how the work was reviewed |
| [docs/ACCESSIBILITY.md](docs/ACCESSIBILITY.md) | Accessibility choices, testing, and known open points |
| [docs/SUSTAINABILITY.md](docs/SUSTAINABILITY.md) | Sustainable design choices, measurements, and what remains unmeasured |
| Review | What to look at |
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| [Running application](http://it2810-29.idi.ntnu.no/project2) | Search, load more, element details, and the Your gymnasts test login |
| [ElementsPage tests](src/tests/pages/ElementsPage.test.tsx) | Search, result paging, URL state, loading, and error behavior |
| [Element detail tests](src/tests/pages/ElementDetailPage.test.tsx) | Catalog detail rendering and error/loading states |
| [Gymnast page tests](src/tests/pages/YourGymnastsPage.test.tsx) | Test login, loading, profile creation, and error handling |
| [Backend catalog tests](backend/src/catalog/search.test.ts) | Search, filters, sorting, pagination, and count limits |
| [Backend server tests](backend/src/server/server.test.ts) | GraphQL API behavior; database-backed tests require a migrated and seeded database |
| [docs/API.md](docs/API.md) | API design, schema, search, pagination, authentication, and errors |
| [docs/AI_USAGE.md](docs/AI_USAGE.md) | How AI was used and how the work was reviewed |
| [docs/ACCESSIBILITY.md](docs/ACCESSIBILITY.md) | Accessibility choices, testing, and known open points |
| [docs/SUSTAINABILITY.md](docs/SUSTAINABILITY.md) | Sustainable design choices, measurements, and what remains unmeasured |

### How to try it

Expand Down Expand Up @@ -219,12 +219,12 @@ fetchMore({ variables: { after: data.searchCatalog.pageInfo.endCursor } })

- **Load more with `fetchMore`.** The cache keeps all the pages of one search in one list (the key is the search, filter and sort, not `first` or `after`). A query with `after` that is sent with `useQuery` or `client.query` is answered from the cache. `fetchMore` always asks the server, so use it for the next page.
- **`totalCount` only with the first page:** the first-page query asks for the count, and later-page queries omit it; the cache keeps the count when those pages are loaded.
- **Authentication.** Session handling (`devLogin`, `logout`, and `restoreSession`) is in `src/lib/apollo/client.ts`. The development test login accepts any email address and is the only login flow planned; do not use real or sensitive data with it. See [Test login on the virtual machine](docs/VM_SETUP.md#6-test-login-on-the-virtual-machine) and [Login for development](docs/API.md#login-for-development). The login page (`src/components/auth/LoginPage.tsx`) offers the demo coach and a test coach of your own. The app reads who is logged in in one place, `useAuth()` (`src/components/auth/useAuth.ts`), which the menu, the protected pages and the gymnasts page share.
- **Authentication.** Session handling (`devLogin`, `logout`, and `restoreSession`) is in `src/lib/apollo/client.ts`. The development test login accepts any email address and is the only login flow planned; do not use real or sensitive data with it. See [Test login on the virtual machine](docs/VM_SETUP.md#6-test-login-on-the-virtual-machine) and [Login for development](docs/API.md#login-for-development). The login page (`src/pages/LoginPage.tsx`) offers the demo coach and a test coach of your own. The app reads who is logged in in one place, `useAuth()` (`src/components/auth/useAuth.ts`), which the menu, the protected pages and the gymnasts page share.
- **Errors.** `describeError(error)` (`src/lib/apollo/errors.ts`) turns an error from the API or the network into a message for the user, and says whether trying again can help.

## The Elements page: search, filters and sort

The Elements page (`/elements`, `src/components/elements/ElementsPage.tsx`) is where a coach searches the catalog. It sends one `searchCatalog` request with the search text, the filters and the sort, and shows the result as a table (cards on a phone) with "Load more elements". The search, the filters and the sort are all in the address (`?search=salto&apparatus=UB&group=UB-6&difficulty=A,B&sort=difficulty&dir=desc`), so a result can be shared and the back button works.
The Elements page (`/elements`, `src/pages/ElementsPage.tsx`) is where a coach searches the catalog. It sends one `searchCatalog` request with the search text, the filters and the sort, and shows the result as a table (cards on a phone) with "Load more elements". The search, the filters and the sort are all in the address (`?search=salto&apparatus=UB&group=UB-6&difficulty=A,B&sort=difficulty&dir=desc`), so a result can be shared and the back button works.

### What the user can do

Expand Down Expand Up @@ -253,7 +253,7 @@ The Elements page (`/elements`, `src/components/elements/ElementsPage.tsx`) is w

### Testing

- `src/components/elements/ElementsPage.test.tsx` runs the page on the real Apollo Client with a stub for `fetch` that answers like the server: each filter, several values, the address in both directions, the disabled filters, clear, the sort list, the filters and the sort together, a wrong address, a structure that fails, and "Load more" with a filter. Each behavior was broken on purpose to check that a test fails without it.
- `src/tests/pages/ElementsPage.test.tsx` runs the page on the real Apollo Client with a stub for `fetch` that answers like the server: each filter, several values, the address in both directions, the disabled filters, clear, the sort list, the filters and the sort together, a wrong address, a structure that fails, and "Load more" with a filter. Each behavior was broken on purpose to check that a test fails without it.
- The backend tests (`backend/src/catalog/search.test.ts`, `backend/src/server/server.test.ts`) cover the vault filter, the range of the values and `Apparatus.difficultyValues`, and need the seeded database like the rest.
- Not everything can be tested in jsdom (for example the tooltip, which Base UI opens from real pointer events). Those parts were checked in a real browser against the real backend.

Expand Down Expand Up @@ -585,15 +585,31 @@ Settings, all optional, as environment variables:

## Project structure

| Folder | Contents |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `backend/` | The backend package: TypeScript, Prisma, the seed script, the catalog search, the GraphQL server and the GraphQL schema |
| `data/` | The catalog data extracted from the Code of Points: JSON files and one image per element |
| `docs/` | Project plans, data model, API, accessibility, sustainability, AI usage, and catalog extraction documentation |
| `e2e/` | The end-to-end tests (Playwright): whole flows in a real browser, accessibility checks and the API over HTTP. See "End-to-end tests" |
| `public/` | Static files served as they are, such as the favicon |
| `scripts/extract-cop/` | The Python script that extracts `data/` from the Code of Points PDF |
| `src/` | The React frontend, shared UI components, API operations, Apollo Client, and frontend tests |
| Folder | Contents |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `backend/` | The backend package: TypeScript, Prisma, the seed script, the catalog search, the GraphQL server and the GraphQL schema |
| `backend/src/` | The backend code, with its tests next to the code they test (`*.test.ts`). See "Where the tests are" below |
| `data/` | The catalog data extracted from the Code of Points: JSON files and one image per element |
| `docs/` | Project plans, data model, API, accessibility, sustainability, AI usage, and catalog extraction documentation |
| `e2e/` | The end-to-end tests (Playwright): whole flows in a real browser, accessibility checks and the API over HTTP. See "End-to-end tests" |
| `public/` | Static files served as they are, such as the favicon |
| `scripts/extract-cop/` | The Python script that extracts `data/` from the Code of Points PDF |
| `src/` | The React frontend, shared UI components, API operations, Apollo Client, and frontend tests |
| `src/pages/` | One file for each route in `src/App.tsx` (the Elements page, the element page, Your gymnasts, Log in and the placeholder pages) |
| `src/components/` | The parts of the pages, in one folder for each part of the site (`elements/`, `gymnasts/`, `auth/`, `navbar/`) |
| `src/components/ui/` | The shadcn/ui components (written by the shadcn/ui CLI) and a few small shared components (`DifficultyBadge`, `ElementValueBadge`, `ElementVariants`) |
| `src/lib/` | The GraphQL operations, Apollo Client and the logic that is not a component |
| `src/tests/` | The frontend tests (Vitest), in the same folders as the code they test, and the shared test helpers. See "Where the tests are" below |

### Where the tests are

The frontend and the backend keep their tests in different places, on purpose:

- **Frontend: `src/tests/`.** The tests are in the same folders as the code they test (`src/tests/pages/ElementsPage.test.tsx` tests `src/pages/ElementsPage.tsx`), and they import it with the `@/` alias. The folder also has what the tests share: `setupTests.ts`, the Apollo test client (`apollo.ts`) and the example rows (`elementRows.ts`). Keeping the tests apart from the code means that `src/pages/` and `src/components/` only show what the app is made of.
- **Backend: next to the code, in `backend/src/`.** `backend/src/catalog/search.test.ts` tests `backend/src/catalog/search.ts`. The backend is its own package, with Node's module system and relative imports (`'../errors.js'`), and its folders are small and grouped by job (`auth/`, `catalog/`, `seed/`, `server/`), so a test next to its file is easy to find. This is the usual layout in a Node package. The tests that use the database need a migrated and seeded database (see "Database (local setup)").
- **End-to-end: `e2e/`** (see "End-to-end tests").

A new test goes in the same place as the others of its kind: a frontend test in `src/tests/` with the same path as the file it tests, and a backend test next to its file.

## Working in this repository

Expand Down
6 changes: 3 additions & 3 deletions docs/ACCESSIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ The “Skip to main content” link is visually hidden until focused. The app us
- The desktop results table has a screen-reader caption, column headers, and `scope="col"` relationships. On narrow screens the results are presented as cards instead.
- Disabled actions for removed catalog items reference the explanatory text with `aria-describedby`.

**Where:** [ElementsPage.tsx](../src/components/elements/ElementsPage.tsx), [ResultsTable.tsx](../src/components/elements/ResultsTable.tsx), [ElementActions.tsx](../src/components/elements/ElementActions.tsx).
**Where:** [ElementsPage.tsx](../src/pages/ElementsPage.tsx), [ResultsTable.tsx](../src/components/elements/ResultsTable.tsx), [ElementActions.tsx](../src/components/elements/ElementActions.tsx).

### Status messages and errors

Dynamic updates such as loading, result counts, and action feedback use status semantics (`role="status"` and, where needed, `aria-live`). Errors use alert semantics so important feedback is exposed to assistive technology.

**Where:** [ElementsPage.tsx](../src/components/elements/ElementsPage.tsx), [ElementActions.tsx](../src/components/elements/ElementActions.tsx), [ElementDetailPage.tsx](../src/components/elements/ElementDetailPage.tsx), and [YourGymnastsPage.tsx](../src/components/gymnasts/YourGymnastsPage.tsx).
**Where:** [ElementsPage.tsx](../src/pages/ElementsPage.tsx), [ElementActions.tsx](../src/components/elements/ElementActions.tsx), [ElementDetailPage.tsx](../src/pages/ElementDetailPage.tsx), and [YourGymnastsPage.tsx](../src/pages/YourGymnastsPage.tsx).

### Themes, contrast, and reduced motion

The interface supports light and dark themes with separate color tokens. Interactive controls and links include visible focus styling. Animations and transitions use reduced-motion variants so they can be suppressed when the user requests reduced motion.

**Where:** theme state in [App.tsx](../src/App.tsx), theme colors in [variables.css](../src/styles/variables.css), and reduced-motion styles in [ElementsPage.tsx](../src/components/elements/ElementsPage.tsx), [ResultsTable.tsx](../src/components/elements/ResultsTable.tsx), and [ElementDetailPage.tsx](../src/components/elements/ElementDetailPage.tsx).
**Where:** theme state in [App.tsx](../src/App.tsx), theme colors in [variables.css](../src/styles/variables.css), and reduced-motion styles in [ElementsPage.tsx](../src/pages/ElementsPage.tsx), [ResultsTable.tsx](../src/components/elements/ResultsTable.tsx), and [ElementDetailPage.tsx](../src/pages/ElementDetailPage.tsx).

The color themes have not yet been comprehensively measured against WCAG contrast criteria. Focus, contrast, zoom, and screen-reader behavior need verification in the complete application.

Expand Down
6 changes: 6 additions & 0 deletions docs/AI_USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,3 +526,9 @@
- **Tool:** Claude Code
- **What the team did:** Found in a review that the element page shows the long description of a vault group under "Element group", and wrote issue #125: show "Group N" for a vault, with the same rule as the Elements list.
- **What Claude did:** Added `number` (and `id`, so Apollo's cache can recognise the group across queries) to the `catalogItem` query, and let the element page use `groupLabel` from the Elements list, so the two pages cannot disagree. Wrote one test for an element and one for a vault (with the real group of VT-1.22-a), and checked that each fails when the page shows the name for every item or "Group N" for every item. After a review of its own work, split one test that checked two cases, and replaced test data for the vault that was half an element.

## The pages in `src/pages/` (`src/pages/`, `src/App.tsx`)

- **Tool:** Claude Code
- **What the team did:** Wrote issue #97: move the pages out of `src/components/`, so it is clear which files are routes and which are parts of pages, and describe the folders in the README.
- **What Claude did:** Moved the four pages and their tests with `git mv` (so their history follows them), and moved the three placeholder pages out of `App.tsx` into their own files, so every route has a file in `src/pages/`. Updated the imports, the links in the README and the docs, and the "Project structure" table. Found that `src/styles/gymnastTheme.test.ts` reads the files of the gymnasts and login pages by folder, so after the move it would have stopped checking the two pages without failing; added them to the list. The shared components in `components/ui/` were left where they are (see the issue). After that, asked to move the frontend tests to `src/tests/` as well: moved all 29 with `git mv` into the same folders as the code they test, changed their relative imports to the `@/` alias (so a test does not depend on where it is), and updated the paths that `gymnastTheme.test.ts` reads files from.
Loading