--- title: Workout tags: workout,overview --- > [TOC] ## Use cases - [ ] Add Terms - https://app.termly.io/ - [x] Create account - [x] Login - [x] Logout - [ ] Show loading before calendar is ready - [ ] Allow reporting duplicate exercise - [ ] Bodyparts overview - change "8 days ago" to "more then week ago" - [ ] Create public version of workout - [ ] Rename workoutlab-3fb75.firebaseapp.com from login screen - [ ] Search exercise names in aliases table - [x] Add map preview to strava workouts list - [ ] Store gpx point encoded in google - [x] Create readonly exercise table - [ ] Allow search using aliases - [x] Strengthlevel ID - [x] Create readonly workout list - [ ] Allow filtering - [ ] With/Without strava - [ ] With/Without sourceFile - [ ] Strava - [ ] Strava list - [ ] Show matching day/duration/calories/time flags - [ ] Show strava workouts which can be mapped to workouts - [ ] Show workouts which can be mapped to strava workouts - [ ] Google api - [ ] add version, buildNumber, buildTime - [ ] to google analytics - [x] to sentry - [ ] User detail - [ ] Add strava id - [ ] Add runkeeper id - [ ] Exercise detail - [ ] hide aliases metadata - [ ] make category metadata standalone field - [ ] Add button "show who practiced this" - [ ] Strength level, stats, my stats > move to menu next to hearth - [ ] Add workout visibility select - [ ] Add workout with multi autocomplete to select users - [ ] Workout detail - [ ] Add export workout button - [ ] Add share button - [ ] - [ ] Exercise record view - [ ] change default textarea view height to be equal number of rows - [ ] remove record overview on mobiles - [ ] Set personal information - [x] Birthday - [x] Height - [x] Weight - [x] Gender - [ ] TargetWeight - [ ] TargetWeightDate - [ ] Calculate BMI https://www.calculatorsoup.com/calculators/health/bmi-calculator.php - [x] Allow to add file (GPX, Image) as attachment to workout - [ ] Allow to create workout from KML format - [ ] Create sidenav menu expandable - https://indepth.dev/tutorials/angular/angular-material-nested-menu-dynamic-data - [ ] for non dev users - [x] hide creation date - [ ] GUI Improvements - [ ] Create user chip - [x] Image preview min-width: min(80vw, 100%); - [ ] Created exercises must be approved by approvers - [ ] Add color to bodyparts ![](https://i.imgur.com/VuA6fdd.png) - [ ] Add functionality from https://www.runloop.com/heavyset - [ ] personl records for reps for each exercise - [ ] Routines with series and reps withoud weights - [ ] Workout overview ![](https://i.imgur.com/qGvbZ0R.png) - [ ] Add functionality from https://play.google.com/store/apps/details?id=com.kg.app.sportdiary&hl=en_GB&gl=US - [ ] Show received achievemnt after workout's record - [ ] Add graph for number of practises for each month for each exercise - [ ] Calendar driven workout overview - [ ] Rename stats: - [ ] Finished exercises/workouts - [ ] Max weight and max reps ![](https://i.imgur.com/o59lrf9.png) - [ ] show max 1 rep weight graph ![](https://i.imgur.com/Ja4giDV.png) - [ ] Create Routine - [ ] Set name - [ ] Add exercises - [ ] Set Reminder - [ ] Start date - [ ] Until - [ ] Number of weeks/days/months - [ ] Specific date - [ ] X repetations - [ ] Send mail notification - [x] Create workout - [x] Add record to workout - [x] Fill properties (start/end date and type, note, place, burned callories) - [x] Save workout - [ ] Create workout from routine - [ ] Choose routine - [ ] Fill values for each exercise record - [ ] Fill missing properties - [ ] Save workout - [x] Show exercise records - [ ] Clone workout into my workouts - [ ] Improve diagrams - ![](https://hackmd.io/_uploads/SycYY5tRn.png) - [ ] Create routine from workout - [ ] Add musscle selector - [x] https://codepen.io/tomaev/pen/dYrEbq - document.querySelectorAll("[class=\"\"]") - [ ] https://codepen.io/baublet/pen/PzjmpL - [ ] https://codepen.io/MikeLouviere/pen/MyyWNe - [ ] Create single record workout creationg page - [ ] Create profile card ![](https://hackmd.io/_uploads/r1h8Z6ZAh.png) - [ ] Add bite flag for workout about exercises bodyparts - [ ] add action row to expandable panel and wrap buttons inside of it - [ ] Each workout has either calculated or user defined burned calories - [ ] Add visibility option in workoute detail - [ ] Handle empty detail actions menu - [x] Show my workouts - [ ] Export my workouts - [x] Edit existing workout - [x] Add record to workout - [x] Remove record from workout - [x] Edit record in workout - [x] Edit workout properties - [x] Save workout - [x] Create exercise record - [x] Choose exercise - [x] Set attributes (weigth, repetations, note) - [x] Save record - [x] Exit exercise record - [x] Change exercise - [x] Edit attributes (weigth, repetations, note) - [x] Save record - [x] Remove existing workout - [ ] Show map for outside activities - [ ] Allow to import exercise record from external application - [ ] Runkeeper - [x] GPX file - [ ] Strava - [ ] - [ ] Show recommendations - [ ] Show which exercies should be practised - [ ] Show week/month/year/total stats - [x] Number of workouts - [ ] Duration - [ ] Information for each exercise - [ ] Duration, count, percentage - [ ] Show stats for exercise - [ ] Maximal repetations/weight - [ ] List of dates when it was practise - [ ] Social - [ ] Search users - [x] Add/remove friend - [ ] Friends are automatically fallowed - [x] Show user detail page ### Create workout #### online ```plantuml start :click on new workout button; :select start date and time; :optionally select end date and time; repeat :click on save button; :send request to server; :wait for response; backward :fix all issues; repeat while (Error response) is (true) -> no; :show success notification; :update store; :redirect to detail of workout; stop ``` #### offline ```plantuml start :click on new workout button; :select start date and time; :optionally select end date and time; repeat :click on save button; :validate workout locally; backward :fix all issues; repeat while (validation failed) is (true) -> no; :save request to localstorage; :show success notification; :update store and add flag that workout is not saved on server; :redirect to detail of workout; stop ``` ## Endpoints ### Workout - `[GET] /workout` - minDate - minimal start date - maxDate - maximal end date - year - get workouts for specific year - month - get workouts for specific month - week - get workouts for specific week - minDuration - minimal duration - maxDuration - maximal duration - bodyPart - filter for workout with exercises for specific body parts - orderBy - creationDate, startDate, duration, callories - orderDirection - asc, desc - `[POST] /workout` - Create new workout - `[GET] workout/:workoutId` - Get workout detail - `[PUT] workout/:workoutId` - Update workout - `[DELETE] workout/:workoutId` - Delete workout - `[GET] workout/:workoutId/exercise_records` - Same as as /exercise_record but for specific workout - `[POST] workout/:workoutId/exercise_records` - Same as as /exercise_record but for specific workout ### Exercises - exercise - Get - Get list of exercises - bodyPart - body part or list of body parts - key - search by name - orderBy - creationDate, name - orderDirection - asc, desc - Post - Create new exercise - exercise/{exerciseId} - Get - Get exercise detail - Put - Update exercise - Delete - Delete exercise ### Exercise records - exercise_record/{exertciseRecordId} - Get - Get exercise record detail - Put - Update exercise record - Delete - Delete exercise record - exercise_record - Get - Get exercise records - Post - Create exercise record ### User - user - Get - Get list of users - key - pattern for user name - orderBy - creationDate, name, numberOfWorkouts, followers - orderDirection - asc, desc - Post - Create new user - user/{userId}/socials/addFriend/{targetUserId} - Post - Add friend to user - user/{userId}/socials/removeFriend/{targetUserId} - Post - Remove friend from user - user/{userId}/socials/follow/{targetUserId} - Post - Follow user - user/{userId}/socials/unfollow/{targetUserId} - Post - Unfollow user - user/{userId}/profile - Get - Get user profile - user/{userId}/stats/{weekly,monthly,yearly} - Get - Get user weekly,monthly,yearly stats - user/{userId}/workout - Get - Same as /workout but for specific user ## Models ### Workout Single workout session. Include multiple exertice records ```plantuml @startmindmap * Workout ** ID ** CreationDate ** StartDate ** EndDate ** Comment ** ExerciseRecords ** BurnedCallories @endmindmap ``` ### Exercise F - Forbitten R - Required O - Optional | Parameter | Create | Update | | -------- | -------- | -------- | | ID | F | R | |Name|R|O| |type|R|O| |bodypart|R|O| |creationDate|F|F| |description|O|O| |createdBy|F|F| |standing|O|O| |outside|O|O| |difficulty|O|O| Exercise definition ```plantuml @startmindmap * Exercise ** Id ** Description ** CreatedBy ** CreationDate ** Image ** Visible for (me/friends/all) ** Bodypart string or bodyparts ** Required/Optional properties *** Duration *** Distance *** Repetitions *** Weight *** Series *** Equipment *** GPX for running *** Avg Heart Rate in BPM @endmindmap ``` ### App stats Exercises: all/approved Bodypart histogram Category histogram Workouts total number Users total number Graphs - number/hours of workouts per months ### Exercise stats #### BasicExerciseStatistics | Name | Type | Description | | -------- | -------- | -------- | | totalNumberOfExercising | number | | | maxRepetitions | number | | | maxWeight | number | | | maxLifted | number | | | maxWeightForReps | `{ [reps in number]: number }` | | | maxRepsForWeight | `{ [weight in number]: number }` | | #### ExerciseStatistics extends [BasicExerciseStatistics](BasicExerciseStatistics) | Name | Type | Description | | -------- | -------- | -------- | | uniqueUsersExercisingThisExercise | number | | | id | ExerciseId | | | updatedTime | string | | | favouriteBy | `{date: string; value: number}` | | ### User stats #### UserExerciseStats extends [BasicExerciseStatistics](BasicExerciseStatistics) | Name | Type | Description | | -------- | -------- | -------- | | exerciseId | ExerciseId | | | userId | UserId | | | updatedTime | string | | | yearStats | `{ [year in number]: BasicExerciseStatistics }` | | | monthStats | `{ [month in number]: BasicExerciseStatistics }` | | #### UserWorkoutStats | Name | Type | Description | | -------- | -------- | -------- | | exerciseHistogram | `[key: string, value: number, exerciseId: ExerciseId][]` | | | bodypartHistogram | `[key: BodyPart, value: number][]` | | | totalExercises | number | | | iniqueExercises | number | | | totalWorkouts | number | | | totalWorkoutDuration | number | | | maxWorkoutDuration | number | | | totalBurnedCalories | number | | | maxBurnedCalories | number | | ### CustomExercise Exercise created by user ```plantuml @startmindmap * CustomExercise ** Extends Exercise ** VerifiedBy *** UserId *** DateOfVerification ** Active/Inactive ** CreatedBy: UserId ** Type *** Weight **** Repetations **** Series **** Weight *** Duration **** Duration *** Run **** Distance **** Duration **** Average speed *** Body weight **** Repetations **** Series @endmindmap ``` ### ExerciseRecord ```plantuml @startmindmap * ExerciseRecord ** ID ** Type:Exercise or CustomExercise ** Date ** Properties for Exercise @endmindmap ``` ### Routine - Template for exercises for one workout - Contains one or more exercises - Cant have reminders ## Exercises ```plantuml @startmindmap * Exertices ** Pushup *** Normal *** Knee pushup *** Pike pushups *** One-legged pushup ** Abs *** Bicycle crunch *** Hollow hold to jackknife *** Plank **** One-leg Plank **** One-arm Plank **** Side plank ***** Side plank with hip abduction ** Squat *** Overhead squat ** Lunge *** Stationary lunge *** Walking lunge *** Jumping lunges *** Elevated pike pushups ** Plank to Downward Dog ** Bird Dog *** Advanced Bird Dog ** Dead bug @endmindmap ``` Knee pushup ## Jobs ### CalculateExerciseStats - cons MIN_RECALCULATION_OFFSET ~= 1 day - find some exercise when metadata are missing - if no one found then find metadat when now() - m.updatedTime > MIN_RECALCULATION_OFFSET - find all workouts where creationDate > m.updateTime and has record with r.exerciseId === m.exerciseId - generate new ExerciseMetadata from workouts - updateDate is now - merge previous and new (or set new if previous are missing) ExerciseMetadata ## Relations ### Database ```plantuml struct WorkoutId struct ExerciseId struct ExerciseRecordId struct UserId class Workout { +id: WorkoutId +createdBy: UserId +ownerId: UserId +member: UserId +participants?: UserId[] +creationDate: DateTime +updateDate: DateTime +deletionDate?: DateTime +sourceFileName?: string +startDate: DateTime +records: WorkoutRecord[] +visibility: PUBLIC | PRIVATE +endDate?: DateTime +burendCalories: number +userDefinedBurnedCalories?: number +averageHeartBear: nubmber; +note: string } enum BodyPart { ABDOMINAL BACK BICEPS CHEST SHOULDERS CALVES LEGS TRICEPS } enum ExerciseType { WEIGHT BODY_WEIGHT DISTANCE DURATION } class Exercise { +id: ExerciseId +description: string +bodypars: BodyPart[] +secondaryBodypars: BodyPart[] +creationDate: DateTime +deletionDate?: DateTime +image?: "storage" +createdBy: UserId +standing?: boolean +outside?: boolean +MET?: number +type: ExerciseType +image: string +variationOf?: ExerciseId +similar?: ExerciseId[] } class ExerciseRecord { +id: ExerciseRecordId +workoutId: WorkoutId +creationDate: DateTime +deletionDate?: DateTime +note?: string +items: ExerciseRecordItem[] } class ExerciseRecordItem { +repetitions?: number +weight?: number +duration?: number +series?: number +distance?: number } ExerciseId <-- ExerciseRecord WorkoutId <-- ExerciseRecord Workout --> WorkoutId Workout --> UserId Workout --> ExerciseId ExerciseId <-- Exercise UserId <-- Exercise Exercise --> ExerciseType Exercise --> BodyPart ExerciseRecordItem <-- ExerciseRecord ExerciseRecordId <-- ExerciseRecord note left of Workout::participants Additional participants (Owner is participant by default) end note note left of Exercise::variationOf Some exercises could be extendsion for another exercises end note hide empty members ``` ### Database - sets ```plantuml enum SetEntryType { +BREAK +DURATION +REPETITIONS } interface SetEntry { +exerciseId: ExerciseId +type: SetEntryType +series?: number +duration?: number +repetitions?: number } class Set { +id: SetId +creationDate: DateTime +createdBy: UserId +entries: SetEntry[] } SetEntryType <-- SetEntry SetEntry <-- Set note right of SetEntry::series If this is omited then value 1 is used end note hide empty members ``` ### Database - metadata ```plantuml interface RepSetEntry { reps: number sets: number count: number } interface DateTimeValue { date: DateTime value: number } interface BasicExerciseMetadata { mostRepsAndSets?: RepSetEntry[] maxRepetitions: number maxWeight: number maxLifted: number maxWeightForReps: Map<number, number> recordsForReps: Map<number, number> } class ExerciseMetadata implements BasicExerciseMetadata { id: ExerciseId updateDate: DateTime favouriteBy: DateTimeValue[] } interface BasicUserExerciseMetadata extends BasicExerciseMetadata { maxWeightOverTime: DateTimeValue[] maxLiftedOverTime: DateTimeValue[] } class UserMetadata { id: UserId updateDate: DateTime longestWorkout: number longestDistance: number fastestPace: number mostBurnedCalories: number minAverageHeartBeat: number maxAverageHeartBeat: number createdExercises: number finishedExercise: number finishedWorkouts: number exerciseRecords: Map<ExerciseId, BasicUserExerciseMetadata> } RepSetEntry <-- BasicExerciseMetadata BasicUserExerciseMetadata <-- UserMetadata note right of BasicExerciseMetadata::mostRepsAndSets Only for exercise of type WEIGHT end note note right of BasicExerciseMetadata::maxRepetitions Maximal repetitions in one series end note note right of BasicExerciseMetadata::maxWeight value from recordsForReps[1] end note note right of BasicExerciseMetadata::maxLifted Series * Reps * Weight end note hide empty members ``` ### API ```plantuml interface NamedUserDTO { +id: UserId +name: string } interface UserDTO extends NamedUserDTO { +avatar: string } interface UserDetailDTO extends UserDTO { +properties: any +preferences: any } interface WorkoutDTO { +id: WorkoutId +startDate: DateTime 'Duration in MS +endDate: DateTime +owner: NamedUserDTO } interface WorkoutDetailDTO extends WorkoutDTO{ +records: ExerciseRecord[] } interface ExerciseDTO { +id: ExerciseId +description: string +bodypars: BodyPart[] +image: string } interface ExerciseDetailDTO extends ExerciseDTO { +creationDate: DateTime } interface ExerciseRecordDTO { +id: ExerciseRecordId +exercise: Exercise +properties: JsonObject +items: WorkoutItemDTO[] } interface WorkoutItemDTO { +weight?: number +repetitions?: number +series?: number +points?: WorkoutItemPointDTO[] } interface WorkoutItemPointDTO { +lat: number +long: number +altitude: number +timeOffset: number } WorkoutItemDTO *-- WorkoutItemPointDTO ExerciseRecordDTO *-- WorkoutItemDTO WorkoutDTO *-- NamedUserDTO WorkoutDetailDTO *-- ExerciseRecordDTO ExerciseRecordDTO *-- ExerciseDTO hide empty members ``` #### API examples ##### Workout detail ###### bench press ```plantuml @startjson { "id": "0caf6d5b-6b45-452b-ac11-bac6181c9578", "startDate": "2023-04-17T07:09:29.780Z", "endDate": "2023-04-17T07:58:29.780Z", "owner": { "id": "fc0a9b94-ef77-46b5-b7aa-31b469608912", "name": "Some User " }, "records": [ { "id":"51e31ea1-d453-416b-a580-61327400a150", "exercise": { "id":"f0d5091b-a7d6-4e58-bc82-134b9b511c22", "name": "Bench press", "bodyparts": ["CHEST"], "image":"https://workout-lab/exercises/f0d5091b-a7d6-4e58-bc82-134b9b511c22/image/320.png" }, "items": [ { "weight": 80, "series": 2, "repetitions": 12 }, { "weight": 80, "series": 2, "repetitions": 10 } ] } ] } @endjson ``` ###### Running ```plantuml @startjson { "id": "0caf6d5b-6b45-452b-ac11-bac6181c9578", "startDate": "2023-04-17T07:09:29.780Z", "endDate": "2023-04-17T07:58:29.780Z", "owner": { "id": "fc0a9b94-ef77-46b5-b7aa-31b469608912", "name": "Some User " }, "records": [ { "id":"51e31ea1-d453-416b-a580-61327400a150", "exercise": { "id":"f0d5091b-a7d6-4e58-bc82-134b9b511c22", "name": "Running", "bodyparts": ["EGS"], "image":"https://workout-lab/exercises/f0d5091b-a7d6-4e58-bc82-134b9b511c22/image/320.png" }, "items": [ { "lat": 80, "long": 2, "altitude": 12, "timeOffset": 0 }, { "lat": 80, "long": 2, "altitude": 13, "timeOffset": 2000 } ] } ] } @endjson ``` ## Operations ### Create template ```plantuml interface Template { +creationDate: string } interface ParsedTemplate { +creationDate: string } interface WorkoutCreation { } hide empty members ``` ```plantuml @startuml box Client participant User participant TemplateComponent participant TemplateService end box box Server participant WorkoutController participant WorkoutService participant ExerciseService end box User -> TemplateComponent: paste text TemplateComponent -> TemplateService: parseTemplate(template) TemplateComponent <- TemplateService: ParsedTemplate User -> TemplateComponent: submit form TemplateComponent -> TemplateService: submitTemplate() TemplateService --> WorkoutController: [POST] WorkoutController -> WorkoutService: processTemplate(template) WorkoutService -> ExerciseService: findExercisesByNames(names) WorkoutService <-- ExerciseService WorkoutController <-- WorkoutService TemplateService <-- WorkoutController: response ==Try to get exercise names== TemplateService -> TemplateComponent: showExercisesTable() opt Some exercises are incorrect loop For each incorect exrcise alt Exercise is second options in reasult User -> TemplateComponent: click context menu User -> TemplateComponent: choose correct exercise else Exercise wasn't found or is missing in found exercises User -> TemplateComponent: click context menu User -> TemplateComponent: click on search button TemplateComponent -> TemplateComponent: showExerciseLookupDialog() User -> TemplateComponent: search and select correct exercise end end end User -> TemplateComponent: confirm exercises TemplateComponent -> TemplateService: WorkoutCreation() TemplateService --> WorkoutController: [POST] WorkoutController -> WorkoutService: createWorkoutCreation(parsedTemplate) WorkoutController <-- WorkoutService TemplateService <-- WorkoutController ==Show creating workout overview== TemplateService -> TemplateComponent: Show overview() opt Some fields are incorrect User -> TemplateComponent: update field end User -> TemplateComponent: click create button TemplateComponent -> TemplateService: createWorkoutFromTemplate(WorkoutCreation, ParsedTemplate) TemplateService --> WorkoutController: [POST] WorkoutController --> WorkoutService: createWorkoutFromTemplate(WorkoutCreation, ParsedTemplate) WorkoutService -> ExerciseService: update searched exercise names and choosen ones inside tabase @enduml ``` ### Import external file ```plantuml @startuml Client --> WorkoutController: processFile(File, ProcessFileOptions) WorkoutController --> WorkoutService: processFile(File, ProcessFileOptions) WorkoutService --> WorkoutService: validateUserPermission(User, PROCESS) WorkoutService --> FileImporter: processFile(File, ProcessFileOptions) FileImporter --> FileImporter: processFileLocaly(File, ProcessFileOptions) FileImporter --> FileProcessor: processFile(File, ProcessFileOptions) FileImporter <- FileProcessor: CompactWorkoutCreateDto FileImporter --> WorkoutBurnedCaloriesCalculator: getCaloriesFor() FileImporter <- WorkoutBurnedCaloriesCalculator: number WorkoutService<- FileImporter: CompactWorkoutCreateDto WorkoutController <- WorkoutService: CompactWorkoutCreateDto Client <-- WorkoutController: CompactWorkoutCreateDto @enduml ``` ### Get workout detail/list ```plantuml @startuml Client --> WorkoutController: getWorkout(WorkoutId) WorkoutController --> WorkoutService: getWorkout(WorkoutId) WorkoutService --> WorkoutService: validateUserPermission(USER, READ) WorkoutService --> WorkoutDatabase: getWorkout(WorkoutId) WorkoutDatabase --> DB WorkoutDatabase <- DB: WorkoutModel WorkoutDatabase -> WorkoutDatabase: WorkoutModelToWorkout(WorkoutModel) WorkoutService <- WorkoutDatabase: SimpleWorkout WorkoutService -> WorkoutService: getWorkoutUsers(SimpleWorkout) WorkoutService -> WorkoutService: getWorkoutExercises(SimpleWorkout) WorkoutService --> ExerciseService:getExercisesByIds(Array<ExerciseId>) WorkoutService --> UserService:getUsersByIds(Array<UserId>) WorkoutService -> WorkoutService: createWorkout(WorkoutModel, Array<Exercise>, Array<User>) WorkoutController <- WorkoutService : Workout Client <- WorkoutController : Workout @enduml ``` ### Create workout ```plantuml @startuml Client --> WorkoutController: createWorkout(WorkoutCreateDto) WorkoutController --> WorkoutService: createWorkout(WorkoutCreateDto, User) WorkoutService --> WorkoutService: validateUserPermission(USER, READ) group is has missing burned calories WorkoutService -> CaloriesCalculator: calculateCalories(WorkoutCreateDto) WorkoutService <- CaloriesCalculator: BurnedCalories WorkoutService -> WorkoutService: setCaloriesToWorkout() end WorkoutService --> WorkoutDatabase: createWorkout(WorkoutCreateDto, User) WorkoutDatabase --> DB WorkoutDatabase <- DB: WorkoutModel WorkoutDatabase -> WorkoutDatabase: WorkoutModelToSimpleWorkout() WorkoutService <- WorkoutDatabase: SimpleWorkout WorkoutService--> WorkoutService: SimpleWorkoutToWorkout() WorkoutController <- WorkoutService : Workout Client <- WorkoutController : Workout @enduml ``` ### Calculate exercise statistics ### Calculate exercise user statistics ### Calculate user statistics ## Screens ### Home screen {%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=201%3A4 %} ### List of exercises {%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=204%3A90 %} ### Exercise detail {%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=203%3A16 %} ### Add exercise ### Add Workout #### Empty workout preview ```plantuml @startsalt {^"Create workout" [Add exercise<&plus>] [<color:#9a9a9a>Save] | [Cancel] } @endsalt ``` #### Add exercise dialog ```plantuml @startsalt {^"Add Exercise record dialog" ^Choose exercise record^ } @endsalt ``` ```plantuml @startsalt {^"Add Exercise record dialog" ^Choose Exercise record^^ Normal pushup^^ Knee pushup^ } @endsalt ``` ```plantuml @startsalt {^"Add Exercise record dialog" ^Normal Pushup^ Repetitions | "12" Series | "5" Duration | " " } @endsalt ``` {%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=284%3A667 %} #### Save workout ```plantuml @startsalt {^"Create workout" Normal pushups (12 * 5) | ["Remove"<&trash>] [Add exercise<&plus>] [Save] | [Cancel] } @endsalt ``` ### Workout detail ### List of workouts {%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=204%3A125 %} ## TODO - [ ] Optimize user activities logging to bulk update - [ ] Add 'create workout' icon to toolbar - [ ] Exercise lookup component - [ ] Add favourite exercises to the menu - [ ] Multi workout overview - [ ] Allow to create empty workout - [ ] Simple way to add exerciseRecord - [x] Hide error notification after logout - [x] Add default permissions for new users - [ ] Create separate endpoint for update exercise records - [ ] Statistics https://codepen.io/marcnewport/pen/xEKWpz - [ ] Realtime recording https://codepen.io/echodean/details/EEyGzZ - [ ] Add more bodyparts - rear_deltoids - soleus_muscle - triceps - traps - lats - mid_back - low_back - glutes - hamstring - calves - quads - abs - forearms - biceps - chest - shoulders - neck ## Features #### Simple overview of your workouts ![](https://hackmd.io/_uploads/rk2ybA6A3.png) ##### Workout statistics ![](https://i.imgur.com/icuMXRl.png) #### Exercise detail ![](https://i.imgur.com/Y2oKsts.png) ##### Exercise statistics ![](https://hackmd.io/_uploads/SkYr-06A2.png) ##### My exercise statistics ![](https://hackmd.io/_uploads/SJys-0TC2.png) #### User profile ##### User properties ![](https://i.imgur.com/rNok6YH.png) ##### Customizable ![](https://i.imgur.com/hcLFM9D.png) ##### User statistics ![](https://hackmd.io/_uploads/BydR-RaC3.png) #### Workout detail ![](https://i.imgur.com/UkMoTkE.png) ##### With simple exercise recods preview ![](https://i.imgur.com/cW7NVML.png) #### Dark/Light theme ![](https://i.imgur.com/PPURUxM.png) #### Workout statistics ![](https://i.imgur.com/JXbRkzV.png) ## Future features #### Offline support #### Advanced workout recommendations #### Realtime mode for breaks timeout, voice commands #### File import (GPX, KML) #### Allow exporting to different formats #### Add share options (facebook, twitter) #### Realtime running tracker (Like Runkeeper) #### Advanced graphs and statistics #### Integration with external apps (Runkeeper, Strava, Huawei Health, Zepp life)