diff --git a/docs/DATA_MODEL.md b/docs/DATA_MODEL.md index 4725459..9a5e0c6 100644 --- a/docs/DATA_MODEL.md +++ b/docs/DATA_MODEL.md @@ -18,6 +18,8 @@ erDiagram APPARATUS ||--o{ ROUTINE : "performed on" ROUTINE }o--o{ ELEMENT : includes ROUTINE }o--o{ VAULT : includes + GYMNAST }o--o{ ELEMENT : "can do or practices" + GYMNAST }o--o{ VAULT : "can do or practices" APPARATUS { string id string name } ELEMENT_GROUP { string id string apparatusId int number string name } @@ -25,7 +27,7 @@ erDiagram VAULT { string id string elementGroupId string code float dValue string description string name string image } VARIANT_GROUP { string id string_array variantIds } COACH { string id string googleId string email string name datetime createdAt } - GYMNAST { string id string coachId string name } + GYMNAST { string id string coachId string name string_array elementIds } ROUTINE { string id string gymnastId string name string apparatusId string_array elementIds } ``` @@ -35,7 +37,7 @@ The model has two parts. **The catalog** Each of the four apparatus (vault, uneven bars, balance beam, floor) is divided into element groups, matching the groups in the Code of Points, for example "1.000 Mounts" on uneven bars. Each group contains either elements (uneven bars, beam, floor) or vaults. Some elements and vaults can be performed in more than one way. These variants are linked together by a variant group. -**The user data** A coach logs in and creates gymnasts. Each gymnast can have several routines on each apparatus, so the coach can compare different versions and their D-scores. A routine is an ordered list of ids: elements on uneven bars, beam and floor, and a single vault on vault. +**The user data** A coach logs in and creates gymnasts. Each gymnast has a list of elements she can do or is practicing, and can have several routines on each apparatus, so the coach can compare different versions and their D-values. A routine is an ordered list of ids: elements on uneven bars, beam and floor, and a single vault on vault. ## Entities @@ -43,92 +45,107 @@ The model has two parts. The four apparatus in women's artistic gymnastics. -| Field | Example | Description | -| --- | --- | --- | -| `id` | `"UB"` | `VT`, `UB`, `BB` or `FX` | +| Field | Example | Description | +| ------ | --------------- | ---------------------------------- | +| `id` | `"UB"` | `VT`, `UB`, `BB` or `FX` | | `name` | `"Uneven bars"` | Name as used in the Code of Points | ### ElementGroup A group of elements or vaults on one apparatus, as listed in the Code of Points. -| Field | Example | Description | -| --- | --- | --- | -| `id` | `"UB-1"` | Apparatus + group number | -| `apparatusId` | `"UB"` | The apparatus the group belongs to | -| `number` | `1` | Group number in the Code of Points | -| `name` | `"Mounts"` | Group name from the Code of Points | +| Field | Example | Description | +| ------------- | ---------- | ---------------------------------- | +| `id` | `"UB-1"` | Apparatus + group number | +| `apparatusId` | `"UB"` | The apparatus the group belongs to | +| `number` | `1` | Group number in the Code of Points | +| `name` | `"Mounts"` | Group name from the Code of Points | ### Element One element on uneven bars, balance beam or floor. -| Field | Example | Description | -| --- | --- | --- | -| `id` | `"UB-1.101-a"` | Apparatus + code + variant letter | -| `elementGroupId` | `"UB-1"` | The group the element belongs to | -| `code` | `"1.101"` | Element number in the Code of Points | -| `value` | `0.1` | Difficulty value (0.1–1.0) | -| `description` | `"Glide kip to support on LB"` | Description from the Code of Points | -| `name` | `"Biles"` or `null` | The gymnast the element is named after, if any | -| `image` | `"UB-1.101-a.webp"` | Drawing of the element | +| Field | Example | Description | +| ---------------- | ------------------------------ | ---------------------------------------------- | +| `id` | `"UB-1.101-a"` | Apparatus + code + variant letter | +| `elementGroupId` | `"UB-1"` | The group the element belongs to | +| `code` | `"1.101"` | Element number in the Code of Points | +| `value` | `0.1` | Difficulty value (0.1–1.0) | +| `description` | `"Glide kip to support on LB"` | Description from the Code of Points | +| `name` | `"Biles"` or `null` | The gymnast the element is named after, if any | +| `image` | `"UB-1.101-a.webp"` | Drawing of the element | ### Vault One vault from the vault table. -| Field | Example | Description | -| --- | --- | --- | -| `id` | `"VT-1.22-a"` | `VT` + code + variant letter | -| `elementGroupId` | `"VT-1"` | The vault group the vault belongs to | -| `code` | `"1.22"` | Vault number in the Code of Points | -| `dValue` | `2.6` | Difficulty score for the vault | -| `description` | `"..."` | Description from the Code of Points | -| `name` | `"Kim"` or `null` | The gymnast the vault is named after, if any | -| `image` | `"VT-1.22-a.webp"` | Drawing of the vault | +| Field | Example | Description | +| ---------------- | ------------------ | -------------------------------------------- | +| `id` | `"VT-1.22-a"` | `VT` + code + variant letter | +| `elementGroupId` | `"VT-1"` | The vault group the vault belongs to | +| `code` | `"1.22"` | Vault number in the Code of Points | +| `dValue` | `2.6` | Difficulty score for the vault | +| `description` | `"..."` | Description from the Code of Points | +| `name` | `"Kim"` or `null` | The gymnast the vault is named after, if any | +| `image` | `"VT-1.22-a.webp"` | Drawing of the vault | ### VariantGroup Links elements or vaults that are different ways of performing the same element. Only elements and vaults with more than one variant belong to a variant group. -| Field | Example | Description | -| --- | --- | --- | -| `id` | `"UB-1.101"` | Apparatus + code. An id starting with `VT-` holds vaults, all others hold elements | -| `variantIds` | `["UB-1.101-a", "UB-1.101-b"]` | The elements or vaults in the group | +| Field | Example | Description | +| ------------ | ------------------------------ | ---------------------------------------------------------------------------------- | +| `id` | `"UB-1.101"` | Apparatus + code. An id starting with `VT-` holds vaults, all others hold elements | +| `variantIds` | `["UB-1.101-a", "UB-1.101-b"]` | The elements or vaults in the group | ### Coach A user of the website. Coaches log in with Google. -| Field | Example | Description | -| --- | --- | --- | -| `id` | | Internal id | -| `googleId` | `"109876543210…"` | Google's unique user id (the `sub` claim in the Google token) | -| `email` | `"kari.nilsen@gmail.com"` | Email from the Google account | -| `name` | `"Kari Nilsen"` | Name from the Google account | -| `createdAt` | `"2026-10-01T12:00:00Z"` | When the coach first logged in | +| Field | Example | Description | +| ----------- | ------------------------- | ------------------------------------------------------------- | +| `id` | | Internal id | +| `googleId` | `"109876543210…"` | Google's unique user id (the `sub` claim in the Google token) | +| `email` | `"kari.nilsen@gmail.com"` | Email from the Google account | +| `name` | `"Kari Nilsen"` | Name from the Google account | +| `createdAt` | `"2026-10-01T12:00:00Z"` | When the coach first logged in | ### Gymnast A gymnast created by a coach. -| Field | Example | Description | -| --- | --- | --- | -| `id` | | Internal id | -| `coachId` | | The coach who created the gymnast | -| `name` | `"Ingrid Haugen"` | Name of the gymnast | +| Field | Example | Description | +| ------------ | ------------------------------------------- | ------------------------------------------------------- | +| `id` | | Internal id | +| `coachId` | | The coach who created the gymnast | +| `name` | `"Ingrid Haugen"` | Name of the gymnast | +| `elementIds` | `["UB-1.101-a", "UB-3.304-a", "VT-1.22-a"]` | Elements and vaults the gymnast can do or is practicing | ### Routine A gymnast's routine on one apparatus. A gymnast can have several routines on the same apparatus. -| Field | Example | Description | -| --- | --- | --- | -| `id` | | Internal id | -| `gymnastId` | | The gymnast the routine belongs to | -| `name` | `"Competition routine"` | Name chosen by the coach, to tell routines apart | -| `apparatusId` | `"UB"` | The apparatus the routine is performed on | -| `elementIds` | `["UB-1.101-a", "UB-3.304-a"]` or `["VT-1.22-a"]` | The elements in the routine, in order. On vault: exactly one vault | +| Field | Example | Description | +| ------------- | ------------------------------------------------- | ------------------------------------------------------------------ | +| `id` | | Internal id | +| `gymnastId` | | The gymnast the routine belongs to | +| `name` | `"Competition routine"` | Name chosen by the coach, to tell routines apart | +| `apparatusId` | `"UB"` | The apparatus the routine is performed on | +| `elementIds` | `["UB-1.101-a", "UB-3.304-a"]` or `["VT-1.22-a"]` | The elements in the routine, in order. On vault: exactly one vault | + +### Calculator routine (not stored) + +The calculator lets anyone put together a routine and see its D-value, without logging in. It has the same shape as a routine, but without an id or a gymnast, and it is never stored in the database. It lives in the client's state while the user is on the page. + +| Field | Example | Description | +| ------------- | ------------------------------ | ------------------------------------------------------------------ | +| `apparatusId` | `"UB"` | The apparatus the routine is for | +| `elementIds` | `["UB-1.101-a", "UB-3.304-a"]` | The elements in the routine, in order. On vault: exactly one vault | + +The D-value is calculated by the backend through a query that takes `apparatusId` and `elementIds` and returns the D-value without saving anything. That way the calculator and saved routines use the same calculation. + +A logged-in coach can save a calculator routine as a routine for one of their gymnasts. It then gets an `id`, a `gymnastId` and a `name`, and is stored like any other routine. + ## Constraints ### Catalog data @@ -148,12 +165,13 @@ These are enforced by the backend. 6. A gymnast can have any number of routines on each apparatus. Routine names should be unique per gymnast and apparatus, so the coach can tell them apart. 7. A routine on vault has exactly one id in `elementIds`, and it is a vault. A routine on any other apparatus contains only elements. 8. All elements in a routine must belong to the same apparatus as the routine. -9. The catalog is open to everyone. Gymnasts and routines can only be read and changed by the coach they belong to. +9. An element appears at most once in a gymnast's `elementIds`. +10. The catalog is open to everyone. Gymnasts and routines can only be read and changed by the coach they belong to. ### Calculated values -10. The difficulty letter (A–J) is not stored. It is calculated from `value`: 0.1 = A, 0.2 = B … 1.0 = J. -11. The D-score of a routine is calculated, not stored. It is the sum of the eight highest values in the routine (`value` for elements, `dValue` for vaults), where each `code` is counted only once. Two variants of the same element therefore only count once, as in the Code of Points. On vault the routine holds one vault, so the D-score is that vault's `dValue`. Composition requirements and connection values are not included yet. +11. The difficulty letter (A–J) is not stored. It is calculated from `value`: 0.1 = A, 0.2 = B … 1.0 = J. +12. The D-value of a routine is calculated, not stored. It is the sum of the eight highest values in the routine (`value` for elements, `dValue` for vaults), where each `code` is counted only once. Two variants of the same element therefore only count once, as in the Code of Points. On vault the routine holds one vault, so the D-value is that vault's `dValue`. Composition requirements and connection values are not included yet. ## Design decisions @@ -163,13 +181,13 @@ Vaults are stored separately from elements because they work differently in the If vaults were stored as elements, they would have a difficulty letter that is always empty. With vault as its own entity, the catalog describes each vault by its D-value, as the Code of Points does. -In a routine, a vault is treated the same way as an element: it is an id in the routine's `elementIds`. A routine on vault holds exactly one vault. This keeps one rule for the D-score on every apparatus (the sum of the eight highest values), and with a single vault in the list, that sum is simply the vault's `dValue`. Vault ids always start with `VT-`, so the backend can tell a vault from an element by its id. +In a routine, a vault is treated the same way as an element: it is an id in the routine's `elementIds`. A routine on vault holds exactly one vault. This keeps one rule for the D-value on every apparatus (the sum of the eight highest values), and with a single vault in the list, that sum is simply the vault's `dValue`. Vault ids always start with `VT-`, so the backend can tell a vault from an element by its id. ### Variants are linked through a variant group Some cells in the Code of Points show two or three ways of performing the same element, each with its own drawing and description, but with the same code and the same value. We store each variant as its own element (`UB-1.101-a`, `UB-1.101-b`) so that each one has its own image and description and can be searched and added to a routine on its own. -The variants are linked through a variant group that holds a list of their ids. Adding or removing a variant only changes the variant group, not the other elements. Because variants share the same code, the D-score counts each code only once (constraint 11). +The variants are linked through a variant group that holds a list of their ids. Adding or removing a variant only changes the variant group, not the other elements. Because variants share the same code, the D-value counts each code only once (constraint 12). We use one list (`variantIds`) instead of separate lists for elements and vaults, because a variant group never contains both. The type is given by the id: vault ids always start with `VT-`. @@ -179,17 +197,27 @@ Coaches log in with their Google account, so we do not store usernames or passwo We store `googleId` and not the token itself. The token expires after a short time and is only used to prove who the user is when they log in. The `googleId` never changes, even if the user changes their name or email. +### A gymnast has a list of elements + +Besides routines, a coach wants an overview of which elements each gymnast can do or is working on. This is stored as a list of element and vault ids on the gymnast. We do not distinguish between elements the gymnast has mastered and elements she is practicing, since the list is meant as a planning overview for the coach. + +The list is independent of the routines: a gymnast can practice an element that is not in any routine yet, and a routine can contain an element the gymnast has not mastered. The list can later be used to suggest elements when the coach builds a routine. + ### A gymnast can have several routines per apparatus -A coach often tries out different versions of a routine, for example with or without a new element, to see how much each version is worth. A gymnast can therefore have any number of routines on the same apparatus, each with its own name. Because the D-score is calculated for each routine, the versions can be compared side by side. +A coach often tries out different versions of a routine, for example with or without a new element, to see how much each version is worth. A gymnast can therefore have any number of routines on the same apparatus, each with its own name. Because the D-value is calculated for each routine, the versions can be compared side by side. ### Elements in a routine are a list of ids A routine stores its elements as an ordered list of element ids. The order in the list is the order in the routine, so no separate position field is needed. If we later need more information per element in a routine (for example connections between elements or coach notes), the list can be changed to a list of objects without affecting the rest of the model. -### The D-score is calculated, not stored +### The D-value is calculated, not stored + +The app is a planning tool: coaches put together routines and see what they are worth. The D-value can always be calculated from the elements in the routine, so it is not stored. A stored copy would go out of date every time a coach changes the routine. It is calculated in the GraphQL resolver when it is requested. Scores from actual competitions are outside the scope of the app. + +### The calculator is a routine that is not saved -The app is a planning tool: coaches put together routines and see what they are worth. The D-score can always be calculated from the elements in the routine, so it is not stored. A stored copy would go out of date every time a coach changes the routine. It is calculated in the GraphQL resolver when it is requested. Scores from actual competitions are outside the scope of the app. +Visitors who are not logged in should also be able to try out routines, but they have no account to save them to. The calculator therefore uses the same structure as a routine (an apparatus and a list of element ids), kept only in the client's state. Because it has the same shape, a logged-in coach can turn a calculator routine into a saved routine without converting anything, and the backend calculates the D-value the same way for both. ### The difficulty letter is calculated diff --git a/docs/UI_PLANS.md b/docs/UI_PLANS.md index 43f60f6..b99f1cf 100644 --- a/docs/UI_PLANS.md +++ b/docs/UI_PLANS.md @@ -2,35 +2,48 @@ ## Brief introduction of the application -We intend to build a web application that helps gymnastics trainers maintain an overview of their gymnasts' routines and the elements they should be practicing. +We are building a web application that helps gymnastics coaches plan their gymnasts' routines. It contains a searchable catalog of all elements and vaults in the FIG Women's Artistic Gymnastics Code of Points 2025–2028. Coaches can put together routines for their gymnasts and see the D-value (difficulty value) of each routine. ## User groups -Our application will mainly support two groups of users. +### Logged-out visitors -### User type 1: The logged-out browser +Anyone who uses the website without logging in. This can be gymnasts, coaches, judges, parents or others with an interest in gymnastics. For them, the website works as a searchable database where they can search, filter, sort and scroll through all elements and vaults, see the details of each one, and try out routines in the calculator. Nothing they do is saved. -The first user group consists of individuals who are logged out of the system. These might include gymnasts who want to browse different elements and their difficulty values. For them, the website functions like a searchable database where they can search, filter, and scroll through various types of elements and skills. +**User stories** -### User type 2: The logged-in trainer +- As a visitor, I want to search for an element by name or description, so that I can quickly find its value. +- As a visitor, I want to filter elements by apparatus and group, so that I only see the elements that are relevant to me. +- As a visitor, I want to see only the elements of one value, so that I can find a new element to practice. +- As a visitor, I want to look up an element's code and value, so that I can check it during or after a competition. +- As a visitor, I want to try out a routine in the calculator, so that I can see what it would be worth. -The second group consists of trainers. Once logged into their trainer account, they can access the site's extended functionality beyond what is publicly available. Trainers can create gymnast profiles, store their data, assign specific elements to a gymnast, or construct full exercises. +### Logged-in coaches -In other words, the application supports trainers in managing gymnasts' workouts and helps optimize their routines to maximize difficulty points (D-values). +Coaches log in with their Google account. In addition to everything a visitor can do, they can create gymnast profiles, build and save routines for each gymnast, and keep track of the elements each gymnast can do or is practicing. + +**User stories** + +- As a coach, I want to create a profile for each of my gymnasts, so that I can keep their routines in one place. +- As a coach, I want to add elements from the catalog to a gymnast's routine, so that I can see the D-value of the routine. +- As a coach, I want to save several versions of a routine on the same apparatus, so that I can compare their D-values. +- As a coach, I want to add elements to a gymnast's list of elements she can do or is practicing, so that I have an overview of each gymnast's skills. ## Pages -### Pages available for logged-in users: +The search page is the start page for both user groups. -- **Your Profile:** Displays account details such as your name and join date. -- **Gymnasts:** An overview of the trainer's gymnasts. Clicking on a gymnast reveals their specific exercises and the elements they are working on. -- **Search:** Allows searching and filtering of different elements, as well as assigning them to gymnasts and their exercises. -- **Calculator:** A dedicated tool for calculating the D-values (Difficulty values) of different exercises. -- **About:** A page describing the website's functionality and purpose. +| Page | Logged out | Logged in | Description | +| ------------------- | ---------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Search** | ✓ | ✓ | Search, filter and sort all elements and vaults. Logged-in coaches can add an element directly to one of their gymnasts' routines, or add it to a gymnast's list of elements. | +| **Element details** | ✓ | ✓ | All information about one element or vault: code, value, description, image, and its variants if it has any. | +| **Calculator** | ✓ | ✓ | Put together a routine on one apparatus and see its D-value. The routine is kept only while the page is open. Logged-in coaches can save it as a routine for one of their gymnasts. | +| **About** | ✓ | ✓ | What the website does, who it is for, and the source of the data. | +| **Log in** | ✓ | | Log in with Google. The first login creates the coach account. | +| **Gymnasts** | | ✓ | Overview of the coach's gymnasts, and a form to create a new gymnast. | +| **Gymnast** | | ✓ | One gymnast's routines on each apparatus with their D-values, and the elements she can do or is practicing. | +| **Your profile** | | ✓ | Account details from Google (name, email) and the date you joined. Log out. | -### Pages available for logged-out users: +## Scope -- **Log in page:** Where users can log into their trainer account. -- **Search:** Allows searching and filtering of different elements in the database. -- **Calculator:** A tool for calculating the D-values of different exercises. -- **About:** A page describing the website's functionality and purpose. \ No newline at end of file +The D-value of a routine is calculated as the sum of the eight highest element values, with each element counted only once. On vault it is the D-value of the chosen vault. Composition requirements and connection values are not included in this version. See [DATA_MODEL.md](DATA_MODEL.md) for details. diff --git a/src/tests/example.test.ts b/src/tests/example.test.ts index 0566964..c1b02b5 100644 --- a/src/tests/example.test.ts +++ b/src/tests/example.test.ts @@ -1,7 +1,7 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest' describe('example', () => { it('adds numbers', () => { - expect(1 + 1).toBe(2); - }); -}); + expect(1 + 1).toBe(2) + }) +})