### Build the calculator page **Goal:** Build the Calculator page from the mockup ([docs/design/calculator.png](docs/design/calculator.png), described in [design/README.md](docs/design/README.md#calculator)). A visitor, logged in or not, puts together a routine on one apparatus and sees its D-value. The routine is kept only in the client while the page is open; nothing is stored. The page is listed in [UI_PLANS.md](docs/UI_PLANS.md), and the routine's shape is in [DATA_MODEL.md](docs/DATA_MODEL.md#calculator-routine-not-stored). The mockup's colours and fonts are placeholders: use the project's existing design tokens and shadcn/ui components. #### Implementation checklist - [ ] **Route and page** - [ ] Add the route `/calculator` and a `CalculatorPage` with the title, the short explanation ("Build a routine on one apparatus and see its D-value. The eight highest element values count; on vault, the routine is one vault.") and the two columns from the mockup, stacked on small screens. - [ ] Link to it from the navbar (#39) when that exists. - [ ] **Apparatus tabs** - [ ] Tabs for Uneven bars, Balance beam, Floor exercise and Vault, using an accessible tabs pattern (arrow keys, `role="tablist"`). - [ ] Changing the apparatus with items in the routine asks for confirmation or clears the routine, because a routine is on one apparatus. Decide which, and say why in `docs/UI_PLANS.md`. - [ ] **"Add elements" list (left column)** - [ ] A search field ("Code, name or description") and a list of the apparatus's elements, with code, name, element group, the difficulty badge and an add button. Use `searchCatalog` filtered on the chosen apparatus, with the "load more" pattern from the search page (#90). - [ ] The add button toggles: "+" to add, a check mark when the element is in the routine (press again to remove). Keep `aria-pressed` and the label in step. - [ ] Announce additions, removals and refusals in a `role="status"` region. - [ ] **Routine panel (right column)** - [ ] A heading ("Uneven bars routine"), the D-value in large type, and "N elements · 8 highest count". - [ ] The ordered list of the routine with number, code, name, difficulty letter and value, and a remove button for each row (with an accessible name that says which element). - [ ] "Not counted" tag on the elements that do not count (see the decision below). - [ ] A "Clear routine" button, and the note "Nothing here is saved. Log in to save routines for your gymnasts." with a link to the login. - [ ] Empty state before the first element, and a clear state for a vault routine (one vault, D-value of that vault). - [ ] **D-value** - [ ] Get the D-value from `calculateDScore(itemIds)`, so the calculator and saved routines use the same calculation. Do not calculate the sum in the frontend. Send one request after each change, and ignore an answer that arrives after a newer request. - [ ] Show loading and error states without losing the routine. Handle `INVALID_ITEMS` and `BAD_USER_INPUT` with a message. - [ ] **State** - [ ] Use the shared calculator-routine state from #91 (the "Try in a routine" button on the element page). The element page and this page must show the same routine, and the order of the items is the order of the routine. - [ ] **Tests** - [ ] Component tests: adding and removing an element updates the list and the panel, the toggle button's label and `aria-pressed`, "Clear routine", and the status messages. - [ ] The apparatus tabs (keyboard), and the confirmation when the apparatus changes. - [ ] The D-value request: sent after a change, a stale answer is ignored, and the error states. - [ ] An accessibility check that the buttons have names and that the page has a sensible heading order. #### Decision needed: "Not counted" The mockup marks the elements that do not count (the ones outside the eight highest values, and a repeated code). `calculateDScore` only returns the sum, so the frontend cannot know which elements are counted without repeating the rule from `backend/src/dscore.ts`. Two ways: 1. **Change the API:** let `calculateDScore` also return which ids count (for example the counted ids next to the sum). One place has the rule, but it changes the schema and the generated types. 2. **Repeat the rule in the frontend:** small, but the same rule then exists twice and can drift apart. The first is the safer choice for the long run. It needs a small backend change and an update of `docs/API.md`. #### Depends on - #91 for the shared calculator-routine state, and the button on the element page that adds to it. - Apollo Client, which is in `dev` (#75). #### Not in this issue - Saving the routine to one of a coach's gymnasts. It needs login and is part of a later issue.