From 816c48bcdc23c1301bb989ae6f3b8d0f1c17b6f8 Mon Sep 17 00:00:00 2001 From: siversm Date: Mon, 9 Nov 2026 16:42:02 +0100 Subject: [PATCH] docs: organize project documentation Split detailed documentation into dedicated Markdown files for setup, testing, performance, architecture, sustainability, and AI use. Keep the README focused on the project overview and contribution guidelines. Co-authored-by: Copilot GPT luna 5.6 --- README.md | 189 ++--------------------------------------- docs/AI decleration.md | 5 ++ docs/Architecture.md | 145 +++++++++++++++++++++++++++++++ docs/Performance.md | 56 ++++++++++++ docs/Setup.md | 98 +++++++++++++++++++++ docs/Testing.md | 58 +++++++++++++ 6 files changed, 369 insertions(+), 182 deletions(-) create mode 100644 docs/Architecture.md create mode 100644 docs/Performance.md create mode 100644 docs/Setup.md create mode 100644 docs/Testing.md diff --git a/README.md b/README.md index 5ec2397..eebe3a3 100644 --- a/README.md +++ b/README.md @@ -14,128 +14,14 @@ In the end we are planning on making the data permanent, and building a better l For our database we use SQLite with Apollo for the GraphQL Handling, this is a very efficient database structure good for a lot of data. SQLite is in some scenarios not recommended, as it does not have it's own database manager software, using only the program it runs in, but in this context, that is not a problem. -## Sustainability choices +For more information, see: -We have made several choices that help reduce unnecessary data transfer, processing, -and storage: - -- **Cursor-based pagination:** - The GraphQL API uses cursors to retrieve Pokémon in - small pages instead of loading the entire dataset at once. Each request is - limited to a maximum of 100 results and returns an `endCursor` and page - indicators, allowing the client to request only the next or previous part of - the dataset. This reduces the amount of data sent and processed for each - interaction, especially when searching or browsing larger datasets. -- **SQLite database:** - SQLite stores the data locally in a single, lightweight - database file. This avoids the overhead of running a separate database server - for this project while still providing structured queries through Apollo - GraphQL. -- **Normalized data structure:** - Pokémon, abilities, types, and ratings are - stored in separate related tables. This reduces duplicated data and makes - updates more efficient, which helps limit storage requirements and - unnecessary database work. - -## Local Setup/install - -We use node/npm, Bun should also work but is not guaranteed. - -To configure commit template run this while in the root of the project (/T26-Project-2) - -```bash -git config --local commit.template .gitmessage -``` - -To setup the rest you can use npm from the project folder: - -```bash -cd project2 -npm install -``` - -and to run the project use these commands (must also be ran from the project2 folder): - -| Command | Description | -|------------------------|----------------------------------------| -| `npm run dev` | Run the project (Frontend) | -| `npm run dev:server` | Run the project (Server) | -| `npm run dev:all` | Run the project (Everything) | -| `npm run build` | Build reactcode into html/js (For VM) | -| `npm run preview` | Preview the built project from `build` | -| `npm run lint` | Run Eslint on the project | -| `npm run format` | Run Prettier on the project | -| `npm run format:check` | Run Prettier on the project no writing | - - -## Production Setup/install - -Running on the Virtual machine requires the machine to have an ssh key linked to your github account. -Since NTNU git really only is stable over ssh. - -To get a key, you can do this: -```bash -ssh-keygen # Enter on every prompt -cat $HOME/.ssh/id_ed25519.pub -``` - -Copy the resulting public key (the entire line including the ssh-ed25519 part on the start, and the username+machinename on the end) -Add the key to https://git.ntnu.no/settings/keys - -Now you can pull the project with these commands (first configuring git to use the key) -```bash -git config core.sshCommand "ssh -i $HOME/.ssh/id_ed25519" -git clone git@git.ntnu.no:IT2810-H26/T26-Project-2.git - -cd T26-Project-2/project2 - -# Installing dependencies -npm install -# Tool to run server, only needed on the VM, so not a project dependency -npm install -g pm2 - -# Let Apache access the dist folder -chmod 755 ~ -chmod -R 755 ~/T26-Project-2/project2/dist/ - -# Symlinking to apache host directory -# Dist must not have anything not publishable -sudo ln -s $HOME/T26-Project-2/project2/dist/ /var/www/html/project2 - -# Setup unattended running with pm2 -npx pm2 start "npm run prod" --name pokemon-api --cwd ~/T26-Project-2/project2 -npx pm2 save -npx pm2 startup - -``` - -**Now the vm should be set up correctly!** - -It should also start the program on boot by using pm2, which is a tool meant for running programs like this unatended. - -To update to new versions, the only thing needed is to pull and restart pm2. -If dependencies are changed, an npm install might also be needed. - -You can either do this manually by doing this: -```bash -cd ~/T26-Project-2/project2 -git pull -npm install -npx pm2 restart pokemon-api -``` - -Or running the script with: -```bash -npm run prod:update -``` - -### Commands specifically for prod - -| Command | Description | -|---------|-------------| -| `npm run prod` | Run prod with correct environment, requires dedicated webserver serving dist | -| `npm run prod:restart` | Restart the pm2 instance, requires VM setup | -| `npm run prod:update` | Pull from github, install new packages if needed, and restart server, requires VM setup | +- [Setup and production deployment](docs/Setup.md) +- [Architecture](docs/Architecture.md) +- [Testing](docs/Testing.md) +- [Performance testing](docs/Performance.md) +- [Sustainability choices](docs/Sustainability.md) +- [AI declaration](docs/AI%20decleration.md) ## Working in this repository @@ -157,64 +43,3 @@ Before a pull request can be merged: 3. **Pushing new commits dismisses the approval**, so if you change something after being approved, ask for another review. This is deliberate: what was approved is what gets merged. - -## Testing - -The application is tested in separate layers so that failures are easier to -understand: - -- **Component tests** verify one React component at a time. They check the - component's rendered output and user interactions without requiring the - GraphQL server or database to run. -- **API tests** will verify GraphQL requests against the server and its - pagination, search, filtering, and sorting behavior. -- **End-to-end tests** will verify a longer browser interaction through the - frontend and API together. - -The project uses Vitest for component tests because it is designed to work with -the existing Vite setup. React Testing Library renders components in a simulated -browser (`jsdom`) and `user-event` performs interactions in the same way a user -would. These dependencies are development-only and do not affect the deployed -application. - -The first component suite tests `Pagination`. Pagination is a good starting -point because it has clear responsibilities and several important edge cases: -the first and last pages must disable the appropriate buttons, page navigation -must call the correct callbacks, the displayed count must be correct, and a -changed page size must be reported to the parent component. - -The second component suite tests `SearchBar`. It verifies that user input is -reported together with the existing search settings, that type filtering and -sorting are sent to the parent component, that sort direction can be toggled, -and that the settings dropdown closes through normal keyboard and mouse -interactions. These tests are valuable because `SearchBar` does not perform -the filtering itself; it must communicate the complete and correct state back -to `App`. - -The third component suite tests `PokemonCard`. It checks that the card presents -the identity and image data supplied by the API, including the padded -Pokédex ID and accessible image alternative text. It also checks that clicking -the card passes the complete Pokémon object to the parent. This keeps the -display and selection behavior reliable without involving the API or the -detail modal. - -Run the component tests from the repository root: - -```bash -npm test -``` - -The root test command forwards the request to the `project2` application, -where the Vitest configuration and test dependencies are located. The tests -can also be run directly from that directory with the same command. - -```bash -cd project2 -npm test -``` - -For an interactive watch mode while developing: - -```bash -npm run test:watch -``` diff --git a/docs/AI decleration.md b/docs/AI decleration.md index be83af5..e698592 100644 --- a/docs/AI decleration.md +++ b/docs/AI decleration.md @@ -15,6 +15,11 @@ understand how these parts of React fit together. AI was also used to help with spelling, formatting, and fact-checking parts of the sustainability documentation. +We also consulted AI while organizing the documentation. It assisted with the +structure and layout of the Markdown files, as well as the language used to +make the documentation clearer and easier to navigate. The team decided what +information to include and reviewed the final wording. + AI was used as a support tool rather than a replacement for understanding the code. The team remains responsible for reviewing the suggestions and for the final implementation and documentation. diff --git a/docs/Architecture.md b/docs/Architecture.md new file mode 100644 index 0000000..fc5a2e1 --- /dev/null +++ b/docs/Architecture.md @@ -0,0 +1,145 @@ +# Architecture + +PokeDox is a React frontend connected to a Node.js GraphQL server. The server +uses SQLite to store Pokémon data and ratings. The frontend is responsible for +the user interface and local browser state, while the backend handles queries, +pagination, filtering, sorting, and database updates. + +> **Suggested picture:** Add a screenshot of the main PokeDox page here. It +> would help explain how the search bar, pagination controls, Pokémon cards, +> rating controls, and detail view fit together. + +## Frontend + +The frontend is built with React, TypeScript, and Vite. `App.tsx` is the main +application component. It keeps track of search input, filters, sorting, +pagination, the selected Pokémon, and ratings. + +The interface is divided into smaller components: + +- `SearchBar` handles search input, type filters, and sorting options. +- `Pagination` handles moving between result pages. +- `PokemonCard` displays a Pokémon in the result grid. +- `PokemonDetail` displays more information about a selected Pokémon. +- `PokemonRating` handles the rating controls. + +The search input is debounced before a new query is sent. The number of cards +per page is also calculated from the available grid space, so the layout can +adapt to different screen sizes. + +Apollo Client connects the frontend to GraphQL. It sends queries and mutations +to the configured API URL and keeps an `InMemoryCache` for data that has +already been requested. The client setup is in `src/main.tsx`. + +Ratings are stored in the browser's `localStorage` as well as being sent to the +server. A browser-generated voter ID connects a user's local rating state to +the corresponding server records. + +## Backend + +The backend runs on Node.js and uses Apollo Server to expose a GraphQL API. It +provides queries for retrieving individual Pokémon and paginated lists, as well +as a mutation for saving ratings. + +The `pokemons` query supports: + +- text search by name; +- filtering by type; +- sorting by Pokémon statistics; +- ascending and descending order; +- cursor-based pagination. + +The server limits a request to a maximum of 100 Pokémon. It returns an +`endCursor`, `hasNextPage`, and `hasPreviousPage` so the frontend can keep +track of the current result window without loading the complete dataset. + +The GraphQL schema and resolver logic are defined in +`server/schema.ts`. The server startup and seeding flow are handled in +`server/index.ts`. + +## Database + +SQLite is used through `better-sqlite3`. The database contains four main +tables: + +- `pokemons` stores the Pokémon identity and statistics. +- `abilities` stores abilities belonging to a Pokémon. +- `types` stores the types belonging to a Pokémon. +- `ratings` stores a rating for a Pokémon and voter combination. + +Foreign keys connect abilities, types, and ratings to the related Pokémon. +Ratings use a composite primary key made from `pokemon_id` and `voter_id`, so +one voter cannot create duplicate rating rows for the same Pokémon. + +The database schema and seed function are in `server/db.ts`. On startup, the +server checks whether Pokémon already exist. If the database is empty, it +fetches data from PokeAPI and falls back to the local JSON data if the external +request fails. + +> **Suggested diagram:** A database diagram would be useful here. It should +> show `pokemons` at the center, with one-to-many relationships to `abilities` +> and `types`, and the `ratings` table connected through `pokemon_id`. It would +> make the foreign keys and the composite rating key easier to understand than +> a text description alone. + +## Frontend-backend communication + +The frontend sends GraphQL requests over HTTP. A typical list request follows +this flow: + +1. The user searches, filters, sorts, or changes page. +2. React updates the relevant state in `App`. +3. Apollo Client sends the `pokemons` query with the current variables. +4. The resolver validates the page size and sorting options. +5. SQLite returns the matching rows. +6. Nested resolvers add abilities, types, and image URLs. +7. Apollo returns the response to React. +8. React renders the updated Pokémon cards. + +When a user submits a rating, the frontend first updates its local rating state +and then sends the `ratePokemon` mutation. If the server request fails, the +browser still retains the local rating and displays an error so the user can +retry. + +## Main data flow + +```text +User interaction + | + v +React components and App state + | + v +Apollo Client + | + v +GraphQL query or mutation + | + v +Apollo Server resolvers + | + v +SQLite database + | + v +GraphQL response + | + v +Updated React interface +``` + +The external PokeAPI is used during initial database seeding rather than for +every user search. Once the data is stored locally, normal application queries +use SQLite. + +> **Suggested architecture diagram:** If the project needs a broader system +> overview, create a diagram showing the browser, React/Apollo Client, +> Apollo GraphQL server, SQLite database, and PokeAPI. This would complement +> the database diagram rather than replace it. + +## Related documentation + +- [Setup](Setup.md) +- [Testing](Testing.md) +- [Performance results](Performance.md) +- [Sustainability choices](Sustainability.md) diff --git a/docs/Performance.md b/docs/Performance.md new file mode 100644 index 0000000..28b87d6 --- /dev/null +++ b/docs/Performance.md @@ -0,0 +1,56 @@ +# Performance results + +These measurements provide a baseline for the application's response time, +transferred data, and production build size. They were collected locally on +9 November 2026 using PowerShell on the development machine. + +## Results + +| Measurement | Repetitions | Result | Context | +|-------------|-------------|--------|---------| +| First page, 10 items | 10 | Average: 16.74 ms; maximum: 147.30 ms | Local GraphQL server | +| Response size, 100 items | 1 | 12,056 bytes | Fields: `id`, `name`, and `imageUrl` | +| Production build | 1 | 448 ms | 571 modules transformed | +| Generated `dist` files | 1 | 2,310,770 bytes | 16 files, including all generated assets | + +## Method + +The API response-time measurement sent the same GraphQL query ten times to the +local server and recorded the elapsed time for each request. The average and +maximum values were then calculated. + +The response-size measurement requested 100 Pokémon with their `id`, `name`, +and `imageUrl` fields. The returned JSON response was saved temporarily and +measured in bytes. + +The build measurement used the project's production build command. Vite +reported the build duration and number of transformed modules, while the +generated files in `dist` were measured together. + +## Sustainability relevance + +These results support the sustainability choices described in +[Sustainability.md](Sustainability.md): + +- The 100-item response was only 12,056 bytes for the selected fields, which + provides a baseline for evaluating the effect of pagination and query size. +- Cursor-based pagination means that users do not need to request the full + dataset for each interaction. This limits the amount of data transferred and + processed at one time. +- The production build completed in 448 ms and produced 2,310,770 bytes of + generated files. These values can be used as a baseline when evaluating + future bundle-size or build optimizations. +- The response-time average was low, but the 147.30 ms maximum was considerably + higher than the average. More repeated runs would be needed before drawing + conclusions about typical worst-case performance. + +The measurements are evidence of the application's current behavior, not a +direct calculation of energy use or carbon emissions. Performance depends on +the machine, database contents, server load, and network conditions. + +## Related implementation + +- [App.tsx](../project2/src/App.tsx) requests paginated GraphQL results. +- [schema.ts](../project2/server/schema.ts) limits page sizes and performs the + server-side queries. +- [main.tsx](../project2/src/main.tsx) configures the Apollo client cache. diff --git a/docs/Setup.md b/docs/Setup.md new file mode 100644 index 0000000..42c3d75 --- /dev/null +++ b/docs/Setup.md @@ -0,0 +1,98 @@ +# Local Setup/install + +We use node/npm, Bun should also work but is not guaranteed. + +To configure commit template run this while in the root of the project (/T26-Project-2) + +```bash +git config --local commit.template .gitmessage +``` + +To setup the rest you can use npm from the project folder: + +```bash +cd project2 +npm install +``` + +and to run the project use these commands (must also be ran from the project2 folder): + +| Command | Description | +|------------------------|----------------------------------------| +| `npm run dev` | Run the project (Frontend) | +| `npm run dev:server` | Run the project (Server) | +| `npm run dev:all` | Run the project (Everything) | +| `npm run build` | Build reactcode into html/js (For VM) | +| `npm run preview` | Preview the built project from `build` | +| `npm run lint` | Run Eslint on the project | +| `npm run format` | Run Prettier on the project | +| `npm run format:check` | Run Prettier on the project no writing | + +## Production Setup/install + +Running on the Virtual machine requires the machine to have an ssh key linked to your github account. +Since NTNU git really only is stable over ssh. + +To get a key, you can do this: +```bash +ssh-keygen # Enter on every prompt +cat $HOME/.ssh/id_ed25519.pub +``` + +Copy the resulting public key (the entire line including the ssh-ed25519 part on the start, and the username+machinename on the end) +Add the key to https://git.ntnu.no/settings/keys + +Now you can pull the project with these commands (first configuring git to use the key) +```bash +git config core.sshCommand "ssh -i $HOME/.ssh/id_ed25519" +git clone git@git.ntnu.no:IT2810-H26/T26-Project-2.git + +cd T26-Project-2/project2 + +# Installing dependencies +npm install +# Tool to run server, only needed on the VM, so not a project dependency +npm install -g pm2 + +# Let Apache access the dist folder +chmod 755 ~ +chmod -R 755 ~/T26-Project-2/project2/dist/ + +# Symlinking to apache host directory +# Dist must not have anything not publishable +sudo ln -s $HOME/T26-Project-2/project2/dist/ /var/www/html/project2 + +# Setup unattended running with pm2 +npx pm2 start "npm run prod" --name pokemon-api --cwd ~/T26-Project-2/project2 +npx pm2 save +npx pm2 startup + +``` + +**Now the vm should be set up correctly!** + +It should also start the program on boot by using pm2, which is a tool meant for running programs like this unatended. + +To update to new versions, the only thing needed is to pull and restart pm2. +If dependencies are changed, an npm install might also be needed. + +You can either do this manually by doing this: +```bash +cd ~/T26-Project-2/project2 +git pull +npm install +npx pm2 restart pokemon-api +``` + +Or running the script with: +```bash +npm run prod:update +``` + +### Commands specifically for prod + +| Command | Description | +|---------|-------------| +| `npm run prod` | Run prod with correct environment, requires dedicated webserver serving dist | +| `npm run prod:restart` | Restart the pm2 instance, requires VM setup | +| `npm run prod:update` | Pull from github, install new packages if needed, and restart server, requires VM setup | diff --git a/docs/Testing.md b/docs/Testing.md new file mode 100644 index 0000000..18513d8 --- /dev/null +++ b/docs/Testing.md @@ -0,0 +1,58 @@ +# Testing + +The application is tested in separate layers so that failures are easier to +understand: + +- **Component tests** verify one React component at a time. They check the + component's rendered output and user interactions without requiring the + GraphQL server or database to run. +- **API tests** will verify GraphQL requests against the server and its + pagination, search, filtering, and sorting behavior. +- **End-to-end tests** will verify a longer browser interaction through the + frontend and API together. + +The project uses Vitest for component tests because it is designed to work with +the existing Vite setup. React Testing Library renders components in a simulated +browser (`jsdom`) and `user-event` performs interactions in the same way a user +would. These dependencies are development-only and do not affect the deployed +application. + +The first component suite tests `Pagination`. Pagination is a good starting +point because it has clear responsibilities and several important edge cases: +the first and last pages must disable the appropriate buttons, page navigation +must call the correct callbacks, the displayed count must be correct, and a +changed page size must be reported to the parent component. + +The second component suite tests `SearchBar`. It verifies that user input is +reported together with the existing search settings, that type filtering and +sorting are sent to the parent component, that sort direction can be toggled, +and that the settings dropdown closes through normal keyboard and mouse +interactions. These tests are valuable because `SearchBar` does not perform +the filtering itself; it must communicate the complete and correct state back +to `App`. + +The third component suite tests `PokemonCard`. It checks that the card presents +the identity and image data supplied by the API, including the padded +Pokédex ID and accessible image alternative text. It also checks that clicking +the card passes the complete Pokémon object to the parent. + +Run the component tests from the repository root: + +```bash +npm test +``` + +The root test command forwards the request to the `project2` application, +where the Vitest configuration and test dependencies are located. The tests +can also be run directly from that directory with the same command. + +```bash +cd project2 +npm test +``` + +For an interactive watch mode while developing: + +```bash +npm run test:watch +```