Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
36bbfa0
refactor: move the pages to src/pages
Nov 5, 2026
b65ef3e
refactor: move the frontend tests to src/tests
Nov 5, 2026
25ab14f
docs: explain where the frontend, backend and end-to-end tests are
Nov 5, 2026
3110903
Merge pull request #154 from IT2810-H26/97-move-the-pages-to-srcpages
xeniag Nov 5, 2026
9732556
style: format the whole project with Prettier
Nov 5, 2026
920bb94
Merge pull request #159 from IT2810-H26/133-run-prettier-on-the-whole…
ronjalh Nov 5, 2026
1cfdae8
Update API URL and Vite proxy configuration for project structure
Nov 5, 2026
871d5ce
feat(ui): add the shadcn/ui tabs component
Nov 6, 2026
9307bf6
refactor(gymnasts): share the confirmation dialog as ConfirmDialog
Nov 6, 2026
a001cda
feat(gymnasts): group the data of one gymnast by apparatus
Nov 6, 2026
9992b75
feat(gymnasts): add the gymnast page
Nov 6, 2026
798ad46
feat(gymnasts): link each gymnast card to the gymnast page
Nov 6, 2026
7d1ea4a
docs: document the AI use for the gymnast page
Nov 6, 2026
eca6cf2
feat(about): add the About page
Nov 6, 2026
bc446f2
test(about): remove the tests of the wording of the page
Nov 6, 2026
555c8bc
docs(ai-usage): note the changes the team asked for on the About page
Nov 6, 2026
807a09e
feat(a11y): add a go-to-top link to every page
Nov 6, 2026
86fbf08
docs(a11y): refer to WCAG 2.4.1 in the bypass block links
Nov 6, 2026
80e24a2
revert: docs(a11y): refer to WCAG 2.4.1 in the bypass block links
Nov 6, 2026
252dbdc
feat(gymnasts): polish the gymnast page and share its buttons and errors
Nov 6, 2026
61bb9d8
feat(elements): dark mode, working Add to gymnast and themed vault badge
Nov 6, 2026
e61a64a
docs: document the gymnast page work, its tests and the e2e run
Nov 6, 2026
44fdba9
refactor(elements): move the add-to-gymnast logic into a hook
Nov 6, 2026
ffd1f8c
docs: document the state and accessibility of the gymnast and element…
Nov 6, 2026
59cecc8
feat(theme): follow the system colour scheme on the first visit
Nov 7, 2026
1dc50df
chore: remove the leftovers of the Vite template
Nov 7, 2026
dac1159
Merge pull request #163 from IT2810-H26/149-gymnast-page
xeniag Nov 9, 2026
9955bef
Merge branch 'dev' into 165-add-a-go-to-top-link-at-the-bottom-of-eve…
xeniag Nov 9, 2026
80bfae8
Merge pull request #166 from IT2810-H26/165-add-a-go-to-top-link-at-t…
xeniag Nov 9, 2026
d1a62c0
docs: remove the deleted App.css from the README
Nov 10, 2026
f594b1f
docs: name the global CSS files correctly in the README
Nov 10, 2026
5a95d87
fix(about): say difficulty instead of grade
Nov 10, 2026
f5c286a
fix: update backend port to 4000 and adjust related documentation
Nov 10, 2026
68e2421
test(theme): restore the stubbed matchMedia after every test
Nov 10, 2026
5e2974b
Merge remote-tracking branch 'origin/dev' into 146-build-the-about-page
Nov 10, 2026
5a58523
chore: restore package-lock.json from before 1cfdae8
Nov 10, 2026
6639767
test(backend): use port 4000 in the test server config
Nov 10, 2026
802dbf3
test: use port 4000 in the sample image URLs
Nov 10, 2026
44a9d6f
Merge pull request #188 from IT2810-H26/156-fix-login-in-vm
ronjalh Nov 10, 2026
1f9a409
Merge pull request #164 from IT2810-H26/146-build-the-about-page
xeniag Nov 10, 2026
255b435
Merge pull request #185 from IT2810-H26/168-follow-the-os-colour-scheme
xeniag Nov 10, 2026
2548cb0
Merge pull request #186 from IT2810-H26/180-remove-the-leftovers-of-t…
xeniag Nov 10, 2026
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
97 changes: 59 additions & 38 deletions README.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ DATABASE_URL="postgresql://wag_app:YOUR_APP_PASSWORD@localhost:5432/wag_catalog"
MIGRATION_DATABASE_URL="postgresql://wag_migrator:YOUR_MIGRATOR_PASSWORD@localhost:5432/wag_catalog"

# The GraphQL server (npm run dev -w backend). All three are optional.
# The port of the server. The backend should be on 3001 on the virtual machine.
# PORT=3001
# The port of the server. Vite and Apache proxy to this port.
# PORT=4000
# The address the drawings are served from, without a final slash. In development
# the server serves them itself. On the virtual machine it is the Apache address,
# for example http://it2810-XX.idi.ntnu.no/project2/images
# IMAGE_BASE_URL=http://localhost:3001/images
# IMAGE_BASE_URL=http://localhost:4000/images
# The addresses that may call the API from a browser, separated by commas.
# CORS_ORIGIN=http://localhost:5173

Expand Down
2 changes: 1 addition & 1 deletion backend/src/seed/demo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe.runIf(hasDatabase)('resetDemoCoach', () => {
contextValue: createContext(
prisma,
{
port: 3001,
port: 4000,
imageBaseUrl: 'http://images.test/images',
corsOrigins: [],
sessionSecret: 'a-secret-that-is-at-least-32-characters-long',
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const minutes = (n: number) => new Date(START.getTime() + n * 60 * 1000)
const days = (n: number) => minutes(n * 24 * 60)

const config: Config = {
port: 3001,
port: 4000,
imageBaseUrl: 'http://images.test/images',
corsOrigins: ['http://allowed.test'],
sessionSecret: 'a-secret-that-is-at-least-32-characters-long',
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server/calculator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { MAX_CALCULATOR_IDS } from './resolvers/calculator.js'

const hasDatabase = Boolean(process.env['DATABASE_URL'])
const config: Config = {
port: 3001,
port: 4000,
imageBaseUrl: 'http://images.test/images',
corsOrigins: [],
}
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type Config = {

export const MIN_SESSION_SECRET_LENGTH = 32

export const DEFAULT_PORT = 3001
export const DEFAULT_PORT = 4000
export const DEFAULT_CORS_ORIGIN = 'http://localhost:5173' // the Vite dev server

/** Reads and checks the settings. A wrong value stops the start-up with a clear message. */
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server/devLogin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const EMAIL = 'test-98-dev-login@example.com'
const NOW = new Date('2026-10-27T10:00:00Z')

const base: Config = {
port: 3001,
port: 4000,
imageBaseUrl: 'http://images.test/images',
corsOrigins: ['http://allowed.test'],
sessionSecret: 'a-secret-that-is-at-least-32-characters-long',
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server/errorCodes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { MAX_QUERY_DEPTH } from './depthLimit.js'
import { INTERNAL_ERROR_MESSAGE } from './formatError.js'

const config: Config = {
port: 3001,
port: 4000,
imageBaseUrl: 'http://images.test/images',
corsOrigins: [],
}
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { loadTypeDefs } from './schema.js'
const hasDatabase = Boolean(process.env['DATABASE_URL'])

const config: Config = {
port: 3001,
port: 4000,
imageBaseUrl: 'http://images.test/images',
corsOrigins: ['http://allowed.test'],
}
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server/userData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const PREFIX = 'test-73-'
const START = new Date('2026-10-17T10:00:00Z')

const config: Config = {
port: 3001,
port: 4000,
imageBaseUrl: 'http://images.test/images',
corsOrigins: [],
sessionSecret: 'a-secret-that-is-at-least-32-characters-long',
Expand Down
20 changes: 17 additions & 3 deletions docs/ACCESSIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,35 @@ 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.

### The Gymnast page and the element page

These pages were checked for a narrow phone and for both themes, in a real browser (`e2e/gymnast-page.e2e.ts`, `e2e/element-page.e2e.ts`):

- **Reflow (WCAG 1.4.10) and text spacing (1.4.12):** no sideways scrolling at 280, 320, 360 and 390 px, also with a name of 56 letters without a space and with the text spacing of 1.4.12.
- **Contrast (1.4.3, 1.4.11):** axe finds no violation in light and dark mode, and the dark mode of the element page and the badges of the vaults. The color under the pointer on a row is measured in the browser, and the text on it can be read.
- **Not only color (1.4.1):** the trash icon turns red on hover and while it is pressed, but its meaning is in its name ("Delete ..."), and a touch screen, which cannot hover, gets the red from the pressed state.
- **Keyboard (2.1.1, 2.4.3, 2.4.7):** the arrow keys, Home and End move between the apparatus tabs; the question "are you sure?" closes with Escape and gives the focus back to the button; after a removal the focus goes to the heading of the list; the menu of "Add to gymnast" opens with the keyboard.
- **Size of what is pressed (2.5.8):** every button, tab and link in the main part is at least 40 px tall and wide on a phone (the criterion asks for 24).
- **Names and roles (4.1.2):** a link that looks like a button is still a link (`LinkButton`); "Add to gymnast" for a visitor who is not logged in is a link with a lock and the text "(log in first)", not a disabled button, which the keyboard cannot reach and which does not say why.

**Where:** [GymnastPage.tsx](../src/pages/GymnastPage.tsx), [ApparatusTabs.tsx](../src/components/gymnasts/ApparatusTabs.tsx), [TrashButton.tsx](../src/components/gymnasts/TrashButton.tsx), [LinkButton.tsx](../src/components/ui/LinkButton.tsx), [AddToGymnast.tsx](../src/components/elements/AddToGymnast.tsx).

### Base UI

The project uses Base UI primitives through the shared button component. This gives buttons a reusable, accessible foundation rather than implementing their basic behavior from scratch. It does not automatically make every composed page or interaction accessible; labels, descriptions, keyboard flow, and visual contrast still need to be designed and checked by the project.
Expand All @@ -61,4 +74,5 @@ Accessibility requirements should be considered while planning each feature and
## Related documentation

- [Sustainable choices](./SUSTAINABILITY.md) – data transfer, image delivery, performance, and maintainability.
- [State management](./STATE_MANAGEMENT.md) – where each kind of state lives, and the shared parts.
- [UI plans](./UI_PLANS.md) – target users and user needs.
Loading