[Pharo] Personal fitness tracker web app.
Promote components to package.
Changed files
- src/MyFitnessTracker-Components/MFTAboutPage.class.st
- src/MyFitnessTracker-Components/MFTActionsCard.class.st
- src/MyFitnessTracker-Components/MFTAnalyticsPage.class.st
- src/MyFitnessTracker-Components/MFTArticle.class.st
- src/MyFitnessTracker-Components/MFTAthletesArticle.class.st
- src/MyFitnessTracker-Components/MFTCard.class.st
- src/MyFitnessTracker-Components/MFTConfirmDialog.class.st
- src/MyFitnessTracker-Components/MFTConfirmForm.class.st
- src/MyFitnessTracker-Components/MFTDateDialog.class.st
- src/MyFitnessTracker-Components/MFTDateFormComponent.class.st
- src/MyFitnessTracker-Components/MFTDefaultFooter.class.st
- src/MyFitnessTracker-Components/MFTDialog.class.st
- src/MyFitnessTracker-Components/MFTDietArticle.class.st
- src/MyFitnessTracker-Components/MFTEmailSignupDialog.class.st
- src/MyFitnessTracker-Components/MFTExercisesForm.class.st
- src/MyFitnessTracker-Components/MFTFooter.class.st
- src/MyFitnessTracker-Components/MFTForm.class.st
- src/MyFitnessTracker-Components/MFTFormComponent.class.st
- src/MyFitnessTracker-Components/MFTGraph.class.st
- src/MyFitnessTracker-Components/MFTHeaderComponent.class.st
- src/MyFitnessTracker-Components/MFTLandingPage.class.st
- src/MyFitnessTracker-Components/MFTLearnPage.class.st
- src/MyFitnessTracker-Components/MFTLog.class.st
- src/MyFitnessTracker-Components/MFTLoginRegisterLogoutComponent.class.st
- src/MyFitnessTracker-Components/MFTLogsPage.class.st
- src/MyFitnessTracker-Components/MFTMainNavigationBarComponent.class.st
- src/MyFitnessTracker-Components/MFTMainPublicCard.class.st
- src/MyFitnessTracker-Components/MFTMainUserCard.class.st
- src/MyFitnessTracker-Components/MFTMusclesArticle.class.st
- src/MyFitnessTracker-Components/MFTPage.class.st
- src/MyFitnessTracker-Components/MFTPrimaryMuscleGroupDialog.class.st
- src/MyFitnessTracker-Components/MFTPrimaryMuscleGroupFormComponent.class.st
- src/MyFitnessTracker-Components/MFTProfilePage.class.st
- src/MyFitnessTracker-Components/MFTPromisesCard.class.st
- src/MyFitnessTracker-Components/MFTPublicLandingPage.class.st
- src/MyFitnessTracker-Components/MFTRegisterProspectEmailHeader.class.st
- src/MyFitnessTracker-Components/MFTRepsDistributionGraph.class.st
- src/MyFitnessTracker-Components/MFTRootComponent.class.st
- src/MyFitnessTracker-Components/MFTSandboxPage.class.st
- src/MyFitnessTracker-Components/MFTScreenComponent.class.st
- src/MyFitnessTracker-Components/MFTSecondaryMuscleGroupsDialog.class.st
- src/MyFitnessTracker-Components/MFTSecondaryMuscleGroupsFormComponent.class.st
- src/MyFitnessTracker-Components/MFTSetDialog.class.st
- src/MyFitnessTracker-Components/MFTSetGroupDialog.class.st
- src/MyFitnessTracker-Components/MFTSetGroupForm.class.st
- src/MyFitnessTracker-Components/MFTSetGroupTask.class.st
- src/MyFitnessTracker-Components/MFTSetGroupsDialog.class.st
- src/MyFitnessTracker-Components/MFTSetGroupsDialogOld.class.st
- src/MyFitnessTracker-Components/MFTSetGroupsFormComponent.class.st
- src/MyFitnessTracker-Components/MFTSetsDialog.class.st
- src/MyFitnessTracker-Components/MFTStringDialog.class.st
- src/MyFitnessTracker-Components/MFTUserForm.class.st
- src/MyFitnessTracker-Components/MFTUserLandingPage.class.st
- src/MyFitnessTracker-Components/MFTUserTask.class.st
- src/MyFitnessTracker-Components/MFTWeightDialog.class.st
- src/MyFitnessTracker-Components/MFTWeightVsTimeGraph.class.st
- src/MyFitnessTracker-Components/MFTWeightsCard.class.st
- src/MyFitnessTracker-Components/MFTWeightsLog.class.st
- src/MyFitnessTracker-Components/MFTWidget.class.st
- src/MyFitnessTracker-Components/MFTWorkoutForm.class.st
- src/MyFitnessTracker-Components/MFTWorkoutTask.class.st
- src/MyFitnessTracker-Components/MFTWorkoutVolumePerWeekGraph.class.st
- src/MyFitnessTracker-Components/MFTWorkoutVolumeVsTimeGraph.class.st
- src/MyFitnessTracker-Components/MFTWorkoutsCard.class.st
- src/MyFitnessTracker-Components/MFTWorkoutsLog.class.st
- src/MyFitnessTracker-Components/package.st
- src/MyFitnessTracker/MFTAboutPage.class.st
- src/MyFitnessTracker/MFTActionsCard.class.st
- src/MyFitnessTracker/MFTAnalyticsPage.class.st
- src/MyFitnessTracker/MFTArticle.class.st
- src/MyFitnessTracker/MFTAthletesArticle.class.st
- src/MyFitnessTracker/MFTCard.class.st
- src/MyFitnessTracker/MFTConfirmDialog.class.st
- src/MyFitnessTracker/MFTConfirmForm.class.st
- src/MyFitnessTracker/MFTDateDialog.class.st
- src/MyFitnessTracker/MFTDateFormComponent.class.st
- src/MyFitnessTracker/MFTDefaultFooter.class.st
- src/MyFitnessTracker/MFTDialog.class.st
- src/MyFitnessTracker/MFTDietArticle.class.st
- src/MyFitnessTracker/MFTEmailSignupDialog.class.st
- src/MyFitnessTracker/MFTExercisesForm.class.st
- src/MyFitnessTracker/MFTFooter.class.st
- src/MyFitnessTracker/MFTForm.class.st
- src/MyFitnessTracker/MFTFormComponent.class.st
- src/MyFitnessTracker/MFTGraph.class.st
- src/MyFitnessTracker/MFTHeaderComponent.class.st
- src/MyFitnessTracker/MFTLandingPage.class.st
- src/MyFitnessTracker/MFTLearnPage.class.st
- src/MyFitnessTracker/MFTLog.class.st
- src/MyFitnessTracker/MFTLoginRegisterLogoutComponent.class.st
- src/MyFitnessTracker/MFTLogsPage.class.st
- src/MyFitnessTracker/MFTMainNavigationBarComponent.class.st
- src/MyFitnessTracker/MFTMainPublicCard.class.st
- src/MyFitnessTracker/MFTMainUserCard.class.st
- src/MyFitnessTracker/MFTMusclesArticle.class.st
- src/MyFitnessTracker/MFTPage.class.st
- src/MyFitnessTracker/MFTPrimaryMuscleGroupDialog.class.st
- src/MyFitnessTracker/MFTPrimaryMuscleGroupFormComponent.class.st
- src/MyFitnessTracker/MFTProfilePage.class.st
- src/MyFitnessTracker/MFTPromisesCard.class.st
- src/MyFitnessTracker/MFTPublicLandingPage.class.st
- src/MyFitnessTracker/MFTRegisterProspectEmailHeader.class.st
- src/MyFitnessTracker/MFTRepsDistributionGraph.class.st
- src/MyFitnessTracker/MFTRootComponent.class.st
- src/MyFitnessTracker/MFTSandboxPage.class.st
- src/MyFitnessTracker/MFTScreenComponent.class.st
- src/MyFitnessTracker/MFTSecondaryMuscleGroupsDialog.class.st
- src/MyFitnessTracker/MFTSecondaryMuscleGroupsFormComponent.class.st
- src/MyFitnessTracker/MFTSetDialog.class.st
- src/MyFitnessTracker/MFTSetGroupDialog.class.st
- src/MyFitnessTracker/MFTSetGroupForm.class.st
- src/MyFitnessTracker/MFTSetGroupTask.class.st
- src/MyFitnessTracker/MFTSetGroupsDialog.class.st
- src/MyFitnessTracker/MFTSetGroupsDialogOld.class.st
- src/MyFitnessTracker/MFTSetGroupsFormComponent.class.st
- src/MyFitnessTracker/MFTSetsDialog.class.st
- src/MyFitnessTracker/MFTStringDialog.class.st
- src/MyFitnessTracker/MFTUserLandingPage.class.st
- src/MyFitnessTracker/MFTUserTask.class.st
- src/MyFitnessTracker/MFTWeightDialog.class.st
- src/MyFitnessTracker/MFTWeightVsTimeGraph.class.st
- src/MyFitnessTracker/MFTWeightsCard.class.st
- src/MyFitnessTracker/MFTWeightsLog.class.st
- src/MyFitnessTracker/MFTWidget.class.st
- src/MyFitnessTracker/MFTWorkoutForm.class.st
- src/MyFitnessTracker/MFTWorkoutTask.class.st
- src/MyFitnessTracker/MFTWorkoutVolumePerWeekGraph.class.st
- src/MyFitnessTracker/MFTWorkoutVolumeVsTimeGraph.class.st
- src/MyFitnessTracker/MFTWorkoutsCard.class.st
- src/MyFitnessTracker/MFTWorkoutsLog.class.st
src/MyFitnessTracker-Components/MFTAboutPage.class.st
@@ -0,0 +1,87 @@
1
Added:
Class {
2
Added:
#name : 'MFTAboutPage',
3
Added:
#superclass : 'MFTPage',
4
Added:
#category : 'MyFitnessTracker-Components-Pages',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Pages'
7
Added:
}
8
Added:
9
Added:
{ #category : 'initialization' }
10
Added:
MFTAboutPage >> initialize [
11
Added:
12
Added:
super initialize.
13
Added:
title := 'about'
14
Added:
]
15
Added:
16
Added:
{ #category : 'rendering' }
17
Added:
MFTAboutPage >> renderBackgroundOn: html [
18
Added:
19
Added:
html heading level1 with: 'Background'.
20
Added:
html heading level2 with: 'Why I built this web app'.
21
Added:
html
22
Added:
paragraph:
23
Added:
'I wanted a convenient way to record, perform data analysis, and create visualizations for my workouts.';
24
Added:
paragraph:
25
Added:
'The following features were, and still are, essential for me:';
26
Added:
unorderedList: [
27
Added:
html
28
Added:
listItem: 'Free access to all app features,';
29
Added:
listItem: 'Easy import and export of my training data,';
30
Added:
listItem:
31
Added:
'An extensible interface to suit a wide range of training routines.' ];
32
Added:
paragraph:
33
Added:
'What better way than to build my own Personal Fitness Tracker?!'.
34
Added:
html heading level2 with: 'Who I am'.
35
Added:
html paragraph:
36
Added:
'I am an engineer working in Europe, with a deep passion for health and fitness. Publishing My Fitness Tracker is a way for me to give thanks to everyone who has inspired me throughtout my fitness journey.'
37
Added:
]
38
Added:
39
Added:
{ #category : 'rendering' }
40
Added:
MFTAboutPage >> renderCatchOn: html [
41
Added:
42
Added:
html heading level1 dangerText with: 'The catch'.
43
Added:
html
44
Added:
paragraph:
45
Added:
'I''ve made some debatable compromises when it comes to My Fitness Tracker:';
46
Added:
unorderedList: [
47
Added:
html
48
Added:
listItem: 'No external advertisement,';
49
Added:
listItem:
50
Added:
'No cookies or personally identifying information stored,';
51
Added:
listItem:
52
Added:
'No paid feature, except for the storage of your training data beyond ten workouts.' ];
53
Added:
paragraph:
54
Added:
'I know these design choices might frustrate and maybe even anger certain users; '
55
Added:
,
56
Added:
'thankfully, all previously existing fitness tracking services do not abide by these design principles.';
57
Added:
paragraph:
58
Added:
'I am developing My Fitness Tracker for my own personal use case, first and foremost; '
59
Added:
,
60
Added:
'secondly, for the benefit of as many other fitness enthusiasts as possible; '
61
Added:
,
62
Added:
'thirdly, for the enjoyment that developing this web application brings me. '
63
Added:
,
64
Added:
'The paid feature enables me to increase the amount of fitness enthusiasts I can cater to.'
65
Added:
]
66
Added:
67
Added:
{ #category : 'rendering' }
68
Added:
MFTAboutPage >> renderContentOn: html [
69
Added:
70
Added:
self renderPageOn: html with: [
71
Added:
self renderVisionOn: html.
72
Added:
self renderBackgroundOn: html.
73
Added:
self renderCatchOn: html ]
74
Added:
]
75
Added:
76
Added:
{ #category : 'rendering' }
77
Added:
MFTAboutPage >> renderVisionOn: html [
78
Added:
79
Added:
html heading level1 with: 'The Vision'.
80
Added:
html paragraph: [
81
Added:
html text: 'Free, and eventually '.
82
Added:
html anchor
83
Added:
url: 'https://fsfe.org/index.en.html';
84
Added:
with: 'libre'.
85
Added:
html text:
86
Added:
' fitness tracking for all to enjoy. No user data tracking and reselling; no features blocked for any reason.' ]
87
Added:
]
src/MyFitnessTracker-Components/MFTActionsCard.class.st
@@ -0,0 +1,18 @@
1
Added:
Class {
2
Added:
#name : 'MFTActionsCard',
3
Added:
#superclass : 'MFTCard',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'rendering' }
10
Added:
MFTActionsCard >> renderContentOn: html [
11
Added:
12
Added:
self renderCardOn: html with: [
13
Added:
html form: [
14
Added:
html formButton
15
Added:
formControl;
16
Added:
callback: [ MFTWorkoutTask new ];
17
Added:
with: 'Log Workout' ] ]
18
Added:
]
src/MyFitnessTracker-Components/MFTAnalyticsPage.class.st
@@ -0,0 +1,70 @@
1
Added:
Class {
2
Added:
#name : 'MFTAnalyticsPage',
3
Added:
#superclass : 'MFTPage',
4
Added:
#instVars : [
5
Added:
'type'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Pages',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Pages'
10
Added:
}
11
Added:
12
Added:
{ #category : 'instance creation' }
13
Added:
MFTAnalyticsPage class >> for: aSymbol [
14
Added:
15
Added:
^ self new
16
Added:
type: aSymbol;
17
Added:
yourself
18
Added:
]
19
Added:
20
Added:
{ #category : 'accessing' }
21
Added:
MFTAnalyticsPage >> analytics [
22
Added:
23
Added:
^ Dictionary newFrom: {
24
Added:
(#workouts -> {
25
Added:
MFTRepsDistributionGraph new.
26
Added:
MFTWorkoutVolumeVsTimeGraph new
27
Added:
"MFTWorkoutVolumePerWeekGraph new" }).
28
Added:
(#weights -> { MFTWeightVsTimeGraph new }) }
29
Added:
]
30
Added:
31
Added:
{ #category : 'rendering' }
32
Added:
MFTAnalyticsPage >> renderActionsButtonGroupOn: html [
33
Added:
34
Added:
html form class: 'my-3'; with: [
35
Added:
html buttonGroup: [
36
Added:
html formButton
37
Added:
bePrimary;
38
Added:
callback: [ self answer ];
39
Added:
with: 'Return' ] ]
40
Added:
]
41
Added:
42
Added:
{ #category : 'rendering' }
43
Added:
MFTAnalyticsPage >> renderContentOn: html [
44
Added:
45
Added:
| components |
46
Added:
components := self analytics at: type.
47
Added:
48
Added:
self renderPageOn: html with: [
49
Added:
self renderActionsButtonGroupOn: html.
50
Added:
html div
51
Added:
style:
52
Added:
'display: flex; flex-wrap: wrap; justify-content: space-around; gap: 1em';
53
Added:
with: [
54
Added:
components do: [ :each |
55
Added:
html card: [
56
Added:
html cardHeader: each title.
57
Added:
html render: each ] ] ] ]
58
Added:
]
59
Added:
60
Added:
{ #category : 'accessing' }
61
Added:
MFTAnalyticsPage >> type [
62
Added:
63
Added:
^ type
64
Added:
]
65
Added:
66
Added:
{ #category : 'accessing' }
67
Added:
MFTAnalyticsPage >> type: anObject [
68
Added:
69
Added:
type := anObject
70
Added:
]
src/MyFitnessTracker-Components/MFTArticle.class.st
@@ -0,0 +1,28 @@
1
Added:
Class {
2
Added:
#name : 'MFTArticle',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'title'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components',
8
Added:
#package : 'MyFitnessTracker-Components'
9
Added:
}
10
Added:
11
Added:
{ #category : 'rendering' }
12
Added:
MFTArticle >> renderArticleOn: html with: aBlock [
13
Added:
14
Added:
html heading: title capitalized.
15
Added:
html render: aBlock
16
Added:
]
17
Added:
18
Added:
{ #category : 'accessing' }
19
Added:
MFTArticle >> title [
20
Added:
21
Added:
^ title
22
Added:
]
23
Added:
24
Added:
{ #category : 'accessing' }
25
Added:
MFTArticle >> title: aString [
26
Added:
27
Added:
title := aString
28
Added:
]
src/MyFitnessTracker-Components/MFTAthletesArticle.class.st
@@ -0,0 +1,25 @@
1
Added:
Class {
2
Added:
#name : 'MFTAthletesArticle',
3
Added:
#superclass : 'MFTArticle',
4
Added:
#instVars : [
5
Added:
'athletes'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components',
8
Added:
#package : 'MyFitnessTracker-Components'
9
Added:
}
10
Added:
11
Added:
{ #category : 'initialization' }
12
Added:
MFTAthletesArticle >> initialize [
13
Added:
14
Added:
super initialize.
15
Added:
title := 'athletes'.
16
Added:
athletes := { MFTMikeMentzer new }
17
Added:
]
18
Added:
19
Added:
{ #category : 'rendering' }
20
Added:
MFTAthletesArticle >> renderContentOn: html [
21
Added:
22
Added:
self
23
Added:
renderArticleOn: html
24
Added:
with: [ athletes do: [ :athlete | athlete renderContentOn: html ] ]
25
Added:
]
src/MyFitnessTracker-Components/MFTCard.class.st
@@ -0,0 +1,21 @@
1
Added:
Class {
2
Added:
#name : 'MFTCard',
3
Added:
#superclass : 'MFTWidget',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'rendering' }
10
Added:
MFTCard >> renderCardOn: html with: aBlock [
11
Added:
12
Added:
html card: aBlock
13
Added:
]
14
Added:
15
Added:
{ #category : 'rendering' }
16
Added:
MFTCard >> renderContentOn: html [
17
Added:
18
Added:
self
19
Added:
renderCardOn: html
20
Added:
with: [ html cardBody: [ html paragraph: self defaultDescription ] ]
21
Added:
]
src/MyFitnessTracker-Components/MFTConfirmDialog.class.st
@@ -0,0 +1,28 @@
1
Added:
Class {
2
Added:
#name : 'MFTConfirmDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Dialogs'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTConfirmDialog >> heading [
11
Added:
12
Added:
^ 'You are about to {1} a {2}:' format: {
13
Added:
action.
14
Added:
component class }
15
Added:
]
16
Added:
17
Added:
{ #category : 'as yet unclassified' }
18
Added:
MFTConfirmDialog >> renderContentOn: html [
19
Added:
20
Added:
self renderDialogOn: html with: [
21
Added:
html render: component.
22
Added:
html label: [
23
Added:
html text: 'Are you sure?'.
24
Added:
action = #delete ifTrue: [
25
Added:
html
26
Added:
space;
27
Added:
strong: 'This action cannot be undone.' ] ] ]
28
Added:
]
src/MyFitnessTracker-Components/MFTConfirmForm.class.st
@@ -0,0 +1,34 @@
1
Added:
Class {
2
Added:
#name : 'MFTConfirmForm',
3
Added:
#superclass : 'MFTForm',
4
Added:
#category : 'MyFitnessTracker-Components-Forms',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Forms'
7
Added:
}
8
Added:
9
Added:
{ #category : 'submorphs - add/remove' }
10
Added:
MFTConfirmForm class >> delete [
11
Added:
12
Added:
^ self new
13
Added:
modelObject: 'Confirm deletion?';
14
Added:
yourself
15
Added:
]
16
Added:
17
Added:
{ #category : 'as yet unclassified' }
18
Added:
MFTConfirmForm >> cancelForm [
19
Added:
20
Added:
self answer: false
21
Added:
]
22
Added:
23
Added:
{ #category : 'initialization' }
24
Added:
MFTConfirmForm >> initialize [
25
Added:
26
Added:
super initialize .
27
Added:
heading := 'Heads up!'
28
Added:
]
29
Added:
30
Added:
{ #category : 'as yet unclassified' }
31
Added:
MFTConfirmForm >> submitForm [
32
Added:
33
Added:
self answer: true
34
Added:
]
src/MyFitnessTracker-Components/MFTDateDialog.class.st
@@ -0,0 +1,50 @@
1
Added:
Class {
2
Added:
#name : 'MFTDateDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'date'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'accessing' }
13
Added:
MFTDateDialog >> date [
14
Added:
15
Added:
^ date
16
Added:
]
17
Added:
18
Added:
{ #category : 'accessing' }
19
Added:
MFTDateDialog >> date: anObject [
20
Added:
21
Added:
date := anObject
22
Added:
]
23
Added:
24
Added:
{ #category : 'initialization' }
25
Added:
MFTDateDialog >> initialize [
26
Added:
27
Added:
super initialize.
28
Added:
date := Date today
29
Added:
]
30
Added:
31
Added:
{ #category : 'rendering' }
32
Added:
MFTDateDialog >> renderContentOn: html [
33
Added:
34
Added:
self renderDialogOn: html with: [
35
Added:
html dateInput5 formControl
36
Added:
value: date yyyymmdd;
37
Added:
callback: [ :value |
38
Added:
date := Date readFrom: value pattern: 'yyyy-mm-dd' ] ]
39
Added:
]
40
Added:
41
Added:
{ #category : 'as yet unclassified' }
42
Added:
MFTDateDialog >> renderSubmitOn: html [
43
Added:
44
Added:
html buttonGroup: [
45
Added:
html formButton
46
Added:
beSubmit;
47
Added:
bePrimary;
48
Added:
callback: [ self answer: date ];
49
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]) ]
50
Added:
]
src/MyFitnessTracker-Components/MFTDateFormComponent.class.st
@@ -0,0 +1,24 @@
1
Added:
Class {
2
Added:
#name : 'MFTDateFormComponent',
3
Added:
#superclass : 'MFTFormComponent',
4
Added:
#category : 'MyFitnessTracker-Components-Forms',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Forms'
7
Added:
}
8
Added:
9
Added:
{ #category : 'initialization' }
10
Added:
MFTDateFormComponent >> initialize [
11
Added:
12
Added:
super initialize.
13
Added:
heading := 'Date'
14
Added:
]
15
Added:
16
Added:
{ #category : 'rendering' }
17
Added:
MFTDateFormComponent >> renderContentOn: html [
18
Added:
19
Added:
self renderFormComponentOn: html with: [
20
Added:
html dateInput5 formControl
21
Added:
value: modelObject date yyyymmdd;
22
Added:
callback: [ :value |
23
Added:
modelObject date: (Date readFrom: value pattern: 'yyyy-mm-dd') ] ]
24
Added:
]
src/MyFitnessTracker-Components/MFTDefaultFooter.class.st
@@ -0,0 +1,18 @@
1
Added:
Class {
2
Added:
#name : 'MFTDefaultFooter',
3
Added:
#superclass : 'MFTFooter',
4
Added:
#category : 'MyFitnessTracker-Components',
5
Added:
#package : 'MyFitnessTracker-Components'
6
Added:
}
7
Added:
8
Added:
{ #category : 'rendering' }
9
Added:
MFTDefaultFooter >> renderContentOn: html [
10
Added:
11
Added:
self renderFooterOn: html with: [
12
Added:
html paragraph: [
13
Added:
html text:
14
Added:
'Copyright 2023–' , Date today year asString , ' Marius PETER '.
15
Added:
html badge beSecondary with: (MFTUtils version
16
Added:
ifNil: [ 'DEVELOP' ]
17
Added:
ifNotNil: [ 'v' , MFTUtils version ]) ] ]
18
Added:
]
src/MyFitnessTracker-Components/MFTDialog.class.st
@@ -0,0 +1,174 @@
1
Added:
Class {
2
Added:
#name : 'MFTDialog',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'heading',
6
Added:
'subheading',
7
Added:
'component',
8
Added:
'action',
9
Added:
'completion'
10
Added:
],
11
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
12
Added:
#package : 'MyFitnessTracker-Components',
13
Added:
#tag : 'Dialogs'
14
Added:
}
15
Added:
16
Added:
{ #category : 'actions' }
17
Added:
MFTDialog class >> add: anObject [
18
Added:
19
Added:
^ self new
20
Added:
action: #add;
21
Added:
component: anObject;
22
Added:
yourself
23
Added:
]
24
Added:
25
Added:
{ #category : 'actions' }
26
Added:
MFTDialog class >> delete: anObject [
27
Added:
28
Added:
^ self new
29
Added:
action: #delete;
30
Added:
component: anObject;
31
Added:
yourself
32
Added:
]
33
Added:
34
Added:
{ #category : 'actions' }
35
Added:
MFTDialog class >> edit: anObject [
36
Added:
37
Added:
^ self new
38
Added:
action: #edit;
39
Added:
component: anObject;
40
Added:
yourself
41
Added:
]
42
Added:
43
Added:
{ #category : 'accessing' }
44
Added:
MFTDialog >> action [
45
Added:
46
Added:
^ action
47
Added:
]
48
Added:
49
Added:
{ #category : 'accessing' }
50
Added:
MFTDialog >> action: anObject [
51
Added:
52
Added:
action := anObject
53
Added:
]
54
Added:
55
Added:
{ #category : 'delegation' }
56
Added:
MFTDialog >> answer [
57
Added:
58
Added:
self answer: component
59
Added:
]
60
Added:
61
Added:
{ #category : 'accessing' }
62
Added:
MFTDialog >> completion [
63
Added:
64
Added:
^ completion
65
Added:
]
66
Added:
67
Added:
{ #category : 'accessing' }
68
Added:
MFTDialog >> completion: anObject [
69
Added:
70
Added:
completion := anObject
71
Added:
]
72
Added:
73
Added:
{ #category : 'accessing' }
74
Added:
MFTDialog >> component [
75
Added:
76
Added:
^ component
77
Added:
]
78
Added:
79
Added:
{ #category : 'accessing' }
80
Added:
MFTDialog >> component: anObject [
81
Added:
82
Added:
component := anObject
83
Added:
]
84
Added:
85
Added:
{ #category : 't - rotating' }
86
Added:
MFTDialog >> heading [
87
Added:
88
Added:
^ action capitalized , ' ' , (heading ifNil: [
89
Added:
('^MFT(.*)Dialog$' asRegex
90
Added:
search: self class name;
91
Added:
subexpression: 2) asCapitalizedPhrase ifNil: [
92
Added:
'Default Component' ] ])
93
Added:
]
94
Added:
95
Added:
{ #category : 'accessing' }
96
Added:
MFTDialog >> heading: aString [
97
Added:
98
Added:
heading := aString
99
Added:
]
100
Added:
101
Added:
{ #category : 'initialization' }
102
Added:
MFTDialog >> initialize [
103
Added:
104
Added:
super initialize.
105
Added:
action := #add
106
Added:
]
107
Added:
108
Added:
{ #category : 'rendering' }
109
Added:
MFTDialog >> renderCompletionOn: html [
110
Added:
111
Added:
completion ifNotNil: [
112
Added:
html progress
113
Added:
class: 'my-3';
114
Added:
with: [
115
Added:
html progressBar
116
Added:
beStriped;
117
Added:
beAnimated;
118
Added:
successBackground;
119
Added:
valueNow: completion;
120
Added:
with: completion percent ] ]
121
Added:
]
122
Added:
123
Added:
{ #category : 'rendering' }
124
Added:
MFTDialog >> renderContentOn: html [
125
Added:
126
Added:
self renderDialogOn: html with: [
127
Added:
html text:
128
Added:
'By default, ' , self class name , ' doesn''t do anything.' ]
129
Added:
]
130
Added:
131
Added:
{ #category : 'rendering' }
132
Added:
MFTDialog >> renderDialogOn: html with: aBlock [
133
Added:
134
Added:
html container
135
Added:
style: 'max-width: 40rem';
136
Added:
with: [
137
Added:
html heading level1 with: self heading.
138
Added:
subheading ifNotNil: [
139
Added:
html heading level3 textMuted with: subheading ].
140
Added:
html form: [
141
Added:
aBlock value.
142
Added:
html horizontalRule.
143
Added:
self renderSubmitOn: html ].
144
Added:
self renderCompletionOn: html ]
145
Added:
]
146
Added:
147
Added:
{ #category : 'rendering' }
148
Added:
MFTDialog >> renderSubmitOn: html [
149
Added:
150
Added:
html buttonGroup: [
151
Added:
html formButton
152
Added:
beSubmit;
153
Added:
bePrimary;
154
Added:
beDangerIf: action = #delete;
155
Added:
callback: [ self answer: true ];
156
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
157
Added:
html formButton
158
Added:
beSubmit;
159
Added:
beSecondary;
160
Added:
callback: [ self answer: false ];
161
Added:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
162
Added:
]
163
Added:
164
Added:
{ #category : 'accessing' }
165
Added:
MFTDialog >> subheading [
166
Added:
167
Added:
^ subheading
168
Added:
]
169
Added:
170
Added:
{ #category : 'accessing' }
171
Added:
MFTDialog >> subheading: aString [
172
Added:
173
Added:
subheading := aString
174
Added:
]
src/MyFitnessTracker-Components/MFTDietArticle.class.st
@@ -0,0 +1,6 @@
1
Added:
Class {
2
Added:
#name : 'MFTDietArticle',
3
Added:
#superclass : 'MFTArticle',
4
Added:
#category : 'MyFitnessTracker-Components',
5
Added:
#package : 'MyFitnessTracker-Components'
6
Added:
}
src/MyFitnessTracker-Components/MFTEmailSignupDialog.class.st
@@ -0,0 +1,66 @@
1
Added:
Class {
2
Added:
#name : 'MFTEmailSignupDialog',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'localPart',
6
Added:
'domain'
7
Added:
],
8
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
9
Added:
#package : 'MyFitnessTracker-Components',
10
Added:
#tag : 'Dialogs'
11
Added:
}
12
Added:
13
Added:
{ #category : 'initialization' }
14
Added:
MFTEmailSignupDialog >> initialize [
15
Added:
16
Added:
super initialize.
17
Added:
localPart := String new.
18
Added:
domain := String new
19
Added:
]
20
Added:
21
Added:
{ #category : 'rendering' }
22
Added:
MFTEmailSignupDialog >> renderContentOn: html [
23
Added:
24
Added:
html div
25
Added:
darkBackground;
26
Added:
paddingTopAndBottom: 3;
27
Added:
paddingLeftAndRight: 5;
28
Added:
class: 'fixed-top';
29
Added:
style: 'z-index: 1052';
30
Added:
with: [
31
Added:
html heading
32
Added:
level3;
33
Added:
whiteText;
34
Added:
with: 'Get notified as soon as MFT is ready!'.
35
Added:
html paragraph
36
Added:
whiteText;
37
Added:
with: [
38
Added:
html
39
Added:
text:
40
Added:
'You will receive a single e-mail indicating that My Fitness Tracker is usable and accepting new user registration.';
41
Added:
space;
42
Added:
text:
43
Added:
'The e-mail address provided shall be used for no other purpose whatsoever.' ].
44
Added:
html form
45
Added:
paddingTopAndBottom: 3;
46
Added:
with: [
47
Added:
html inputGroup with: [
48
Added:
html formTextInput
49
Added:
callback: [ :value | localPart := value ];
50
Added:
placeholder: 'my-user'.
51
Added:
html inputGroupAppend: [ html inputGroupText: '@' ].
52
Added:
html formTextInput
53
Added:
callback: [ :value | domain := value ];
54
Added:
placeholder: 'example.com'.
55
Added:
html inputGroupAppend: [
56
Added:
html buttonGroup: [
57
Added:
html formButton
58
Added:
beSecondary;
59
Added:
beSubmit;
60
Added:
callback: [ self answer: localPart , '@' , domain ];
61
Added:
with: 'Confirm'.
62
Added:
html outlineButton
63
Added:
beSecondary;
64
Added:
callback: [ self answer: nil ];
65
Added:
with: 'Cancel' ] ] ] ] ]
66
Added:
]
src/MyFitnessTracker-Components/MFTExercisesForm.class.st
@@ -0,0 +1,46 @@
1
Added:
Class {
2
Added:
#name : 'MFTExercisesForm',
3
Added:
#superclass : 'MFTForm',
4
Added:
#category : 'MyFitnessTracker-Components-Forms',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Forms'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTExercisesForm class >> forExerciseCount: anInteger [
11
Added:
12
Added:
| exercises |
13
Added:
exercises := OrderedCollection new: anInteger.
14
Added:
1 to: anInteger do: [ :i |
15
Added:
exercises add: (MFTSQLite3Database allExercises at: i) ].
16
Added:
^ self new
17
Added:
modelObject: exercises;
18
Added:
yourself
19
Added:
]
20
Added:
21
Added:
{ #category : 'initialization' }
22
Added:
MFTExercisesForm >> initialize [
23
Added:
24
Added:
super initialize.
25
Added:
modelObject := OrderedCollection new.
26
Added:
heading := 'Exercises'
27
Added:
]
28
Added:
29
Added:
{ #category : 'rendering' }
30
Added:
MFTExercisesForm >> renderContentOn: html [
31
Added:
32
Added:
self renderFormOn: html with: [ self renderSelectExercisesOn: html ]
33
Added:
]
34
Added:
35
Added:
{ #category : 'rendering' }
36
Added:
MFTExercisesForm >> renderSelectExercisesOn: html [
37
Added:
38
Added:
modelObject doWithIndex: [ :selectedExercise :i |
39
Added:
html formGroup: [
40
Added:
html label: 'Exercise ' , i asString.
41
Added:
html select formControl
42
Added:
list: MFTSQLite3Database allExercises;
43
Added:
selected: selectedExercise;
44
Added:
callback: [ :exercise | modelObject at: i put: exercise ];
45
Added:
labels: [ :each | each englishName ] ] ]
46
Added:
]
src/MyFitnessTracker-Components/MFTFooter.class.st
@@ -0,0 +1,27 @@
1
Added:
Class {
2
Added:
#name : 'MFTFooter',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#category : 'MyFitnessTracker-Components',
5
Added:
#package : 'MyFitnessTracker-Components'
6
Added:
}
7
Added:
8
Added:
{ #category : 'rendering' }
9
Added:
MFTFooter >> renderContentOn: html [
10
Added:
11
Added:
self
12
Added:
renderFooterOn: html
13
Added:
with: [ html text: 'I am the default footer' ]
14
Added:
]
15
Added:
16
Added:
{ #category : 'rendering' }
17
Added:
MFTFooter >> renderFooterOn: html with: aBlock [
18
Added:
19
Added:
html div
20
Added:
marginTop: 3;
21
Added:
paddingTopAndBottom: 3;
22
Added:
class: 'bg-light';
23
Added:
with: [
24
Added:
html container
25
Added:
class: 'd-flex justify-content-center';
26
Added:
with: [ aBlock value ] ]
27
Added:
]
src/MyFitnessTracker-Components/MFTForm.class.st
@@ -0,0 +1,85 @@
1
Added:
"
2
Added:
I represent a user-fillable form.
3
Added:
4
Added:
Upon submitting the form (""submit"" button), I return (`WAComponent>>#answer:`) the `#modelObject` with its attributes populated by the appropriate `MFTFormComponent` subclasses.
5
Added:
Upon canceling the form (""cancel"" button), I return `nil`.
6
Added:
"
7
Added:
Class {
8
Added:
#name : 'MFTForm',
9
Added:
#superclass : 'SBSComponent',
10
Added:
#instVars : [
11
Added:
'modelObject',
12
Added:
'heading'
13
Added:
],
14
Added:
#category : 'MyFitnessTracker-Components-Forms',
15
Added:
#package : 'MyFitnessTracker-Components',
16
Added:
#tag : 'Forms'
17
Added:
}
18
Added:
19
Added:
{ #category : 'as yet unclassified' }
20
Added:
MFTForm >> cancelForm [
21
Added:
22
Added:
self answer: nil
23
Added:
]
24
Added:
25
Added:
{ #category : 'initialization' }
26
Added:
MFTForm >> initialize [
27
Added:
28
Added:
super initialize.
29
Added:
heading := 'Form heading'
30
Added:
]
31
Added:
32
Added:
{ #category : 'accessing' }
33
Added:
MFTForm >> modelObject [
34
Added:
35
Added:
^ modelObject
36
Added:
]
37
Added:
38
Added:
{ #category : 'accessing' }
39
Added:
MFTForm >> modelObject: anObject [
40
Added:
41
Added:
modelObject := anObject
42
Added:
]
43
Added:
44
Added:
{ #category : 'rendering' }
45
Added:
MFTForm >> renderContentOn: html [
46
Added:
47
Added:
self renderFormOn: html with: [ html render: modelObject ]
48
Added:
]
49
Added:
50
Added:
{ #category : 'as yet unclassified' }
51
Added:
MFTForm >> renderFormOn: html with: aBlock [
52
Added:
53
Added:
html container
54
Added:
style: 'max-width: 40rem';
55
Added:
with: [
56
Added:
html displayHeading
57
Added:
level: 3;
58
Added:
with: heading.
59
Added:
html form: [
60
Added:
aBlock value.
61
Added:
html horizontalRule.
62
Added:
self renderSubmitOn: html ] ]
63
Added:
]
64
Added:
65
Added:
{ #category : 'as yet unclassified' }
66
Added:
MFTForm >> renderSubmitOn: html [
67
Added:
68
Added:
html buttonGroup: [
69
Added:
html formButton
70
Added:
beSubmit;
71
Added:
bePrimary;
72
Added:
callback: [ self submitForm ];
73
Added:
with: 'Submit'.
74
Added:
html formButton
75
Added:
beSubmit;
76
Added:
beSecondary;
77
Added:
callback: [ self cancelForm ];
78
Added:
with: 'Cancel' ]
79
Added:
]
80
Added:
81
Added:
{ #category : 'as yet unclassified' }
82
Added:
MFTForm >> submitForm [
83
Added:
84
Added:
self answer: modelObject
85
Added:
]
src/MyFitnessTracker-Components/MFTFormComponent.class.st
@@ -0,0 +1,92 @@
1
Added:
"
2
Added:
When instantiated with the `MFTFormComponent class>>#set:` message, I return an instance of the appropriate subclass for setting the model object attribute.
3
Added:
"
4
Added:
Class {
5
Added:
#name : 'MFTFormComponent',
6
Added:
#superclass : 'SBSComponent',
7
Added:
#instVars : [
8
Added:
'heading',
9
Added:
'modelObject'
10
Added:
],
11
Added:
#category : 'MyFitnessTracker-Components-Forms',
12
Added:
#package : 'MyFitnessTracker-Components',
13
Added:
#tag : 'Forms'
14
Added:
}
15
Added:
16
Added:
{ #category : 'as yet unclassified' }
17
Added:
MFTFormComponent class >> formComponentFor: anAttribute [
18
Added:
19
Added:
| formComponents |
20
Added:
formComponents := Dictionary newFrom: {
21
Added:
(#date -> MFTDateFormComponent).
22
Added:
(#setGroups -> MFTSetGroupsFormComponent).
23
Added:
(#primaryMuscleGroup
24
Added:
-> MFTPrimaryMuscleGroupFormComponent).
25
Added:
(#secondaryMuscleGroups
26
Added:
-> MFTSecondaryMuscleGroupsFormComponent) }.
27
Added:
28
Added:
^ formComponents at: anAttribute ifAbsent: [
29
Added:
self error:
30
Added:
('no appropriate {1} subclass for setting attribute "{2}"'
31
Added:
format: {
32
Added:
self.
33
Added:
anAttribute }) ]
34
Added:
]
35
Added:
36
Added:
{ #category : 'as yet unclassified' }
37
Added:
MFTFormComponent class >> set: anAttribute [
38
Added:
39
Added:
| formComponent |
40
Added:
formComponent := self formComponentFor: anAttribute.
41
Added:
^ formComponent new
42
Added:
modelObject: thisContext sender receiver modelObject;
43
Added:
yourself
44
Added:
]
45
Added:
46
Added:
{ #category : 'accessing' }
47
Added:
MFTFormComponent >> heading [
48
Added:
49
Added:
^ heading
50
Added:
]
51
Added:
52
Added:
{ #category : 'accessing' }
53
Added:
MFTFormComponent >> heading: anObject [
54
Added:
55
Added:
heading := anObject
56
Added:
]
57
Added:
58
Added:
{ #category : 'initialization' }
59
Added:
MFTFormComponent >> initialize [
60
Added:
61
Added:
super initialize
62
Added:
]
63
Added:
64
Added:
{ #category : 'accessing' }
65
Added:
MFTFormComponent >> modelObject [
66
Added:
67
Added:
^ modelObject
68
Added:
]
69
Added:
70
Added:
{ #category : 'accessing' }
71
Added:
MFTFormComponent >> modelObject: anObject [
72
Added:
73
Added:
modelObject := anObject
74
Added:
]
75
Added:
76
Added:
{ #category : 'rendering' }
77
Added:
MFTFormComponent >> renderContentOn: html [
78
Added:
79
Added:
self renderFormComponentOn: html with: [ html render: modelObject ]
80
Added:
]
81
Added:
82
Added:
{ #category : 'as yet unclassified' }
83
Added:
MFTFormComponent >> renderFormComponentOn: html with: aBlock [
84
Added:
85
Added:
html formGroup
86
Added:
class: 'my-4';
87
Added:
with: [
88
Added:
html paragraph
89
Added:
heading: 3;
90
Added:
with: [ html text: heading ].
91
Added:
aBlock value ]
92
Added:
]
src/MyFitnessTracker-Components/MFTGraph.class.st
@@ -0,0 +1,72 @@
1
Added:
Class {
2
Added:
#name : 'MFTGraph',
3
Added:
#superclass : 'MFTWidget',
4
Added:
#instVars : [
5
Added:
'labels',
6
Added:
'data'
7
Added:
],
8
Added:
#category : 'MyFitnessTracker-Components-Widgets',
9
Added:
#package : 'MyFitnessTracker-Components',
10
Added:
#tag : 'Widgets'
11
Added:
}
12
Added:
13
Added:
{ #category : 'accessing' }
14
Added:
MFTGraph >> data [
15
Added:
16
Added:
^ data
17
Added:
]
18
Added:
19
Added:
{ #category : 'accessing' }
20
Added:
MFTGraph >> data: anObject [
21
Added:
22
Added:
data := anObject
23
Added:
]
24
Added:
25
Added:
{ #category : 'initialization' }
26
Added:
MFTGraph >> initialize [
27
Added:
28
Added:
super initialize.
29
Added:
labels := #( Monday Tuesday Wednesday Thursday Friday ).
30
Added:
data := #( 5 1 6 2 7 )
31
Added:
]
32
Added:
33
Added:
{ #category : 'accessing' }
34
Added:
MFTGraph >> labels [
35
Added:
36
Added:
^ labels
37
Added:
]
38
Added:
39
Added:
{ #category : 'accessing' }
40
Added:
MFTGraph >> labels: anObject [
41
Added:
42
Added:
labels := anObject
43
Added:
]
44
Added:
45
Added:
{ #category : 'rendering' }
46
Added:
MFTGraph >> renderContentOn: html [
47
Added:
48
Added:
html heading level5 with: self defaultDescription.
49
Added:
html canvas id: 'defaultGraph'.
50
Added:
html script:
51
Added:
'var ctx = document.getElementById("defaultGraph").getContext("2d");
52
Added:
var defaultChart = new Chart(ctx, {
53
Added:
type: "line",
54
Added:
data: {
55
Added:
labels: ' , labels asJavascript , ',
56
Added:
datasets: [{
57
Added:
label: "default data",
58
Added:
data: ' , data asJavascript , ',
59
Added:
borderWidth: 1
60
Added:
}]
61
Added:
}
62
Added:
});'
63
Added:
]
64
Added:
65
Added:
{ #category : 'accessing' }
66
Added:
MFTGraph >> title [
67
Added:
68
Added:
^ ('^MFT(.*)Graph$' asRegex
69
Added:
search: self class name;
70
Added:
subexpression: 2) asCapitalizedPhrase ifNil: [
71
Added:
'Default Graph' ]
72
Added:
]
src/MyFitnessTracker-Components/MFTHeaderComponent.class.st
@@ -0,0 +1,6 @@
1
Added:
Class {
2
Added:
#name : 'MFTHeaderComponent',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#category : 'MyFitnessTracker-Components',
5
Added:
#package : 'MyFitnessTracker-Components'
6
Added:
}
src/MyFitnessTracker-Components/MFTLandingPage.class.st
@@ -0,0 +1,55 @@
1
Added:
Class {
2
Added:
#name : 'MFTLandingPage',
3
Added:
#superclass : 'MFTPage',
4
Added:
#instVars : [
5
Added:
'cards'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Pages',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Pages'
10
Added:
}
11
Added:
12
Added:
{ #category : 'accessing' }
13
Added:
MFTLandingPage >> cards [
14
Added:
15
Added:
^ cards
16
Added:
]
17
Added:
18
Added:
{ #category : 'accessing' }
19
Added:
MFTLandingPage >> cards: aMFTCardCollection [
20
Added:
21
Added:
cards := aMFTCardCollection
22
Added:
]
23
Added:
24
Added:
{ #category : 'hooks' }
25
Added:
MFTLandingPage >> children [
26
Added:
27
Added:
^ cards
28
Added:
]
29
Added:
30
Added:
{ #category : 'as yet unclassified' }
31
Added:
MFTLandingPage >> defaultCards [
32
Added:
33
Added:
^ {
34
Added:
MFTCard new.
35
Added:
MFTCard new.
36
Added:
MFTCard new }
37
Added:
]
38
Added:
39
Added:
{ #category : 'as yet unclassified' }
40
Added:
MFTLandingPage >> initialize [
41
Added:
42
Added:
super initialize.
43
Added:
title := 'home'.
44
Added:
cards := self defaultCards
45
Added:
]
46
Added:
47
Added:
{ #category : 'rendering' }
48
Added:
MFTLandingPage >> renderContentOn: html [
49
Added:
50
Added:
self renderPageOn: html with: [
51
Added:
html div
52
Added:
style:
53
Added:
'display: flex; flex-wrap: wrap; justify-content: space-around; gap: 1em';
54
Added:
with: [ self cards do: [ :card | html render: card ] ] ]
55
Added:
]
src/MyFitnessTracker-Components/MFTLearnPage.class.st
@@ -0,0 +1,35 @@
1
Added:
Class {
2
Added:
#name : 'MFTLearnPage',
3
Added:
#superclass : 'MFTPage',
4
Added:
#instVars : [
5
Added:
'articles',
6
Added:
'activeArticle'
7
Added:
],
8
Added:
#category : 'MyFitnessTracker-Components-Pages',
9
Added:
#package : 'MyFitnessTracker-Components',
10
Added:
#tag : 'Pages'
11
Added:
}
12
Added:
13
Added:
{ #category : 'initialization' }
14
Added:
MFTLearnPage >> initialize [
15
Added:
16
Added:
super initialize.
17
Added:
title := 'learn'.
18
Added:
articles := {
19
Added:
MFTAthletesArticle new.
20
Added:
MFTMusclesArticle new }.
21
Added:
activeArticle := articles first
22
Added:
]
23
Added:
24
Added:
{ #category : 'rendering' }
25
Added:
MFTLearnPage >> renderContentOn: html [
26
Added:
27
Added:
self renderPageOn: html with: [
28
Added:
html navigation flexColumn with: [
29
Added:
articles do: [ :article |
30
Added:
html navigationLink
31
Added:
beActiveIf: [ activeArticle = article ];
32
Added:
callback: [ activeArticle := article ];
33
Added:
with: article title capitalized ] ].
34
Added:
html render: activeArticle ]
35
Added:
]
src/MyFitnessTracker-Components/MFTLog.class.st
@@ -0,0 +1,28 @@
1
Added:
Class {
2
Added:
#name : 'MFTLog',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'title'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Logs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Logs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'rendering' }
13
Added:
MFTLog >> renderLogOn: html with: aBlock [
14
Added:
15
Added:
html div: aBlock
16
Added:
]
17
Added:
18
Added:
{ #category : 'accessing' }
19
Added:
MFTLog >> title [
20
Added:
21
Added:
^ title
22
Added:
]
23
Added:
24
Added:
{ #category : 'accessing' }
25
Added:
MFTLog >> title: anObject [
26
Added:
27
Added:
title := anObject
28
Added:
]
src/MyFitnessTracker-Components/MFTLoginRegisterLogoutComponent.class.st
@@ -0,0 +1,154 @@
1
Added:
Class {
2
Added:
#name : 'MFTLoginRegisterLogoutComponent',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'user'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components',
8
Added:
#package : 'MyFitnessTracker-Components'
9
Added:
}
10
Added:
11
Added:
{ #category : 'as yet unclassified' }
12
Added:
MFTLoginRegisterLogoutComponent >> attemptLogin: aMFTUser [
13
Added:
"Attempt to match aMFTUser passwordHash against an existing user password_hash in the database."
14
Added:
15
Added:
| existingUser |
16
Added:
aMFTUser username = 'testMFT' ifTrue: [
17
Added:
^ self session login: MFTSQLite3Database getTestUser ].
18
Added:
existingUser := MFTSQLite3Database getExistingUser: aMFTUser.
19
Added:
aMFTUser passwordHash = existingUser passwordHash
20
Added:
ifTrue: [ self session login: existingUser ]
21
Added:
ifFalse: [ self inform: 'Login failed' ]
22
Added:
]
23
Added:
24
Added:
{ #category : 'initialization' }
25
Added:
MFTLoginRegisterLogoutComponent >> initialize [
26
Added:
27
Added:
super initialize.
28
Added:
user := MFTUser new
29
Added:
]
30
Added:
31
Added:
{ #category : 'as yet unclassified' }
32
Added:
MFTLoginRegisterLogoutComponent >> logout [
33
Added:
34
Added:
self session logout
35
Added:
]
36
Added:
37
Added:
{ #category : 'rendering' }
38
Added:
MFTLoginRegisterLogoutComponent >> renderContentOn: html [
39
Added:
40
Added:
self session user isLoggedIn
41
Added:
ifFalse: [ self renderLoginAndRegisterModalsOn: html ]
42
Added:
ifTrue: [ self renderLogoutButtonOn: html ]
43
Added:
]
44
Added:
45
Added:
{ #category : 'rendering' }
46
Added:
MFTLoginRegisterLogoutComponent >> renderLoginAndRegisterModalsOn: html [
47
Added:
"Logging in and registering display modal components; they are not in a form."
48
Added:
49
Added:
html buttonGroup: [
50
Added:
html outlineButton
51
Added:
beSecondary;
52
Added:
beSmall;
53
Added:
dataToggle: 'modal';
54
Added:
dataTarget: '#loginModal';
55
Added:
with: 'Login'.
56
Added:
html outlineButton
57
Added:
bePrimary;
58
Added:
beSmall;
59
Added:
dataToggle: 'modal';
60
Added:
dataTarget: '#registerModal';
61
Added:
with: 'Register' ].
62
Added:
self renderLoginModalOn: html.
63
Added:
self renderRegisterModalOn: html
64
Added:
]
65
Added:
66
Added:
{ #category : 'rendering' }
67
Added:
MFTLoginRegisterLogoutComponent >> renderLoginModalOn: html [
68
Added:
69
Added:
html modal
70
Added:
id: 'loginModal';
71
Added:
fade;
72
Added:
with: [
73
Added:
html modalDialog
74
Added:
beCentered;
75
Added:
with: [
76
Added:
html modalContent: [
77
Added:
html form: [
78
Added:
html modalHeader: [
79
Added:
html modalTitle
80
Added:
level5;
81
Added:
with: 'Welcome back!'.
82
Added:
html modalCloseButton ].
83
Added:
html modalBody with: [
84
Added:
html formGroup: [
85
Added:
html label: 'Username'.
86
Added:
html textInput formControl callback: [ :value |
87
Added:
user username: value ] ].
88
Added:
html formGroup: [
89
Added:
html label: 'Password'.
90
Added:
html passwordInput formControl callback: [ :value |
91
Added:
user passwordHashFromString: value ] ] ].
92
Added:
html modalFooter: [
93
Added:
html buttonGroup: [
94
Added:
html formButton
95
Added:
bePrimary;
96
Added:
callback: [ self attemptLogin: user ];
97
Added:
with: 'Login'.
98
Added:
html formButton
99
Added:
beSecondary;
100
Added:
dataDismiss: 'modal';
101
Added:
with: 'Cancel' ] ] ] ] ] ]
102
Added:
]
103
Added:
104
Added:
{ #category : 'rendering' }
105
Added:
MFTLoginRegisterLogoutComponent >> renderLogoutButtonOn: html [
106
Added:
"Logging out is an action; the button is in a form."
107
Added:
108
Added:
html form: [
109
Added:
html outlineButton
110
Added:
beSecondary;
111
Added:
beSmall;
112
Added:
callback: [ self logout ];
113
Added:
with: 'Logout' ]
114
Added:
]
115
Added:
116
Added:
{ #category : 'rendering' }
117
Added:
MFTLoginRegisterLogoutComponent >> renderRegisterModalOn: html [
118
Added:
119
Added:
html modal
120
Added:
id: 'registerModal';
121
Added:
fade;
122
Added:
with: [
123
Added:
html modalDialog
124
Added:
beCentered;
125
Added:
with: [
126
Added:
html modalContent: [
127
Added:
html form: [
128
Added:
html modalHeader: [
129
Added:
html modalTitle
130
Added:
level5;
131
Added:
with: 'Welcome aboard!'.
132
Added:
html modalCloseButton ].
133
Added:
html modalBody with: [
134
Added:
html formGroup: [
135
Added:
html label: 'Username'.
136
Added:
html textInput formControl callback: [ :value |
137
Added:
user username: value ] ].
138
Added:
html formGroup: [
139
Added:
html label: 'Password'.
140
Added:
html passwordInput formControl callback: [ :value |
141
Added:
user passwordHashFromString: value ] ] ].
142
Added:
html modalFooter: [
143
Added:
html buttonGroup: [
144
Added:
html formButton
145
Added:
bePrimary;
146
Added:
callback: [
147
Added:
MFTSQLite3Database registerNewUser: user.
148
Added:
self attemptLogin: user ];
149
Added:
with: 'Register'.
150
Added:
html formButton
151
Added:
beSecondary;
152
Added:
dataDismiss: 'modal';
153
Added:
with: 'Cancel' ] ] ] ] ] ]
154
Added:
]
src/MyFitnessTracker-Components/MFTLogsPage.class.st
@@ -0,0 +1,49 @@
1
Added:
Class {
2
Added:
#name : 'MFTLogsPage',
3
Added:
#superclass : 'MFTPage',
4
Added:
#instVars : [
5
Added:
'activeLog',
6
Added:
'logs'
7
Added:
],
8
Added:
#category : 'MyFitnessTracker-Components-Pages',
9
Added:
#package : 'MyFitnessTracker-Components',
10
Added:
#tag : 'Pages'
11
Added:
}
12
Added:
13
Added:
{ #category : 'hooks' }
14
Added:
MFTLogsPage >> children [
15
Added:
16
Added:
^ logs
17
Added:
]
18
Added:
19
Added:
{ #category : 'initialization' }
20
Added:
MFTLogsPage >> initialize [
21
Added:
22
Added:
super initialize.
23
Added:
title := 'logs'.
24
Added:
logs := {
25
Added:
MFTWorkoutsLog new.
26
Added:
MFTWeightsLog new }.
27
Added:
activeLog := logs first
28
Added:
]
29
Added:
30
Added:
{ #category : 'rendering' }
31
Added:
MFTLogsPage >> renderContentOn: html [
32
Added:
33
Added:
self renderPageOn: html with: [
34
Added:
self renderLogNavigationOn: html.
35
Added:
html render: activeLog ]
36
Added:
]
37
Added:
38
Added:
{ #category : 'rendering' }
39
Added:
MFTLogsPage >> renderLogNavigationOn: html [
40
Added:
41
Added:
html form
42
Added:
class: 'mb-3';
43
Added:
with: [
44
Added:
html navigation flexColumn with: [
45
Added:
logs do: [ :log |
46
Added:
html navigationLink
47
Added:
callback: [ activeLog := log ];
48
Added:
with: log title ] ] ]
49
Added:
]
src/MyFitnessTracker-Components/MFTMainNavigationBarComponent.class.st
@@ -0,0 +1,64 @@
1
Added:
Class {
2
Added:
#name : 'MFTMainNavigationBarComponent',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'loginRegisterLogout'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components',
8
Added:
#package : 'MyFitnessTracker-Components'
9
Added:
}
10
Added:
11
Added:
{ #category : 'hooks' }
12
Added:
MFTMainNavigationBarComponent >> children [
13
Added:
14
Added:
^ { loginRegisterLogout }
15
Added:
]
16
Added:
17
Added:
{ #category : 'initialization' }
18
Added:
MFTMainNavigationBarComponent >> initialize [
19
Added:
20
Added:
super initialize.
21
Added:
loginRegisterLogout := MFTLoginRegisterLogoutComponent new
22
Added:
]
23
Added:
24
Added:
{ #category : 'rendering' }
25
Added:
MFTMainNavigationBarComponent >> renderContentOn: html [
26
Added:
27
Added:
html navigationBar
28
Added:
beLight;
29
Added:
lightBackground;
30
Added:
expandLarge;
31
Added:
marginBottom: 3;
32
Added:
paddingLeftAndRight: 3;
33
Added:
style: 'position: sticky; top: 0; z-index: 1051';
34
Added:
with: [ self renderNavigationBarItemsOn: html ]
35
Added:
]
36
Added:
37
Added:
{ #category : 'rendering' }
38
Added:
MFTMainNavigationBarComponent >> renderNavigationBarItemsOn: html [
39
Added:
40
Added:
| pm |
41
Added:
pm := self session pageManager.
42
Added:
html navigationBarBrand
43
Added:
callback: [ pm setActivePageAt: #home ];
44
Added:
with: 'MyFitnessTracker'.
45
Added:
"The toggler is only visible when the screen width is reduced."
46
Added:
html navigationBarToggler
47
Added:
beButtonType;
48
Added:
collapse;
49
Added:
dataTarget: '#navbarCollapsed';
50
Added:
with: [ html navigationBarTogglerIcon ].
51
Added:
html navigationBarCollapse collapse
52
Added:
id: 'navbarCollapsed';
53
Added:
class: 'justify-content-between';
54
Added:
with: [
55
Added:
html navigationBarNavigation: [
56
Added:
pm mainPages keysAndValuesDo: [ :name :page |
57
Added:
html navigationItem
58
Added:
beActiveIf: [ pm activePage = page ];
59
Added:
with: [
60
Added:
html navigationLink
61
Added:
callback: [ pm setActivePageAt: name ];
62
Added:
with: page title capitalized ] ] ].
63
Added:
html render: loginRegisterLogout ]
64
Added:
]
src/MyFitnessTracker-Components/MFTMainPublicCard.class.st
@@ -0,0 +1,45 @@
1
Added:
Class {
2
Added:
#name : 'MFTMainPublicCard',
3
Added:
#superclass : 'MFTCard',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'adding' }
10
Added:
MFTMainPublicCard >> addWorkout [
11
Added:
12
Added:
| newWorkout |
13
Added:
newWorkout := self session pageManager activePage call:
14
Added:
MFTWorkoutForm new.
15
Added:
newWorkout
16
Added:
ifNil: [ ^ self ]
17
Added:
ifNotNil: [ self session pageManager setActivePageAt: #logs ].
18
Added:
19
Added:
self session user isLoggedIn
20
Added:
ifTrue: [ self session db insert: newWorkout ]
21
Added:
ifFalse: [ self session user workouts add: newWorkout ]
22
Added:
]
23
Added:
24
Added:
{ #category : 'rendering' }
25
Added:
MFTMainPublicCard >> renderContentOn: html [
26
Added:
27
Added:
self renderCardOn: html with: [
28
Added:
html jumbotron
29
Added:
class: 'p-3 bg-light h-100';
30
Added:
with: [
31
Added:
html displayHeading
32
Added:
level: 1;
33
Added:
with: 'Log, plan, share your workouts.'.
34
Added:
html heading
35
Added:
level: 2;
36
Added:
textMuted;
37
Added:
with: 'Free Software never looked so fit!'.
38
Added:
html form: [
39
Added:
html formButton
40
Added:
bePrimary;
41
Added:
beLarge;
42
Added:
class: 'mt-3';
43
Added:
callback: [ self addWorkout ];
44
Added:
with: 'Log a workout' ] ] ]
45
Added:
]
src/MyFitnessTracker-Components/MFTMainUserCard.class.st
@@ -0,0 +1,41 @@
1
Added:
Class {
2
Added:
#name : 'MFTMainUserCard',
3
Added:
#superclass : 'MFTCard',
4
Added:
#instVars : [
5
Added:
'lastLoginDate',
6
Added:
'subtitle'
7
Added:
],
8
Added:
#category : 'MyFitnessTracker-Components-Widgets',
9
Added:
#package : 'MyFitnessTracker-Components',
10
Added:
#tag : 'Widgets'
11
Added:
}
12
Added:
13
Added:
{ #category : 'initialization' }
14
Added:
MFTMainUserCard >> initialize [
15
Added:
16
Added:
super initialize.
17
Added:
lastLoginDate := self session user lastLoginDateAndTime.
18
Added:
subtitle := 'Last logged in {1}, {2} at {3} UTC' format: {
19
Added:
lastLoginDate dayOfWeekName.
20
Added:
lastLoginDate asDate asString.
21
Added:
lastLoginDate asTimeUTC asString }
22
Added:
]
23
Added:
24
Added:
{ #category : 'rendering' }
25
Added:
MFTMainUserCard >> renderContentOn: html [
26
Added:
27
Added:
| user |
28
Added:
user := self session user.
29
Added:
self renderCardOn: html with: [
30
Added:
html jumbotron
31
Added:
class: 'p-3 bg-light';
32
Added:
with: [
33
Added:
html displayHeading
34
Added:
level: 1;
35
Added:
with: [
36
Added:
html
37
Added:
text: 'Welcome, ';
38
Added:
code: (user realFirstName ifEmpty: [ user username ]);
39
Added:
text: '!' ].
40
Added:
html big textMuted with: subtitle ] ]
41
Added:
]
src/MyFitnessTracker-Components/MFTMusclesArticle.class.st
@@ -0,0 +1,30 @@
1
Added:
Class {
2
Added:
#name : 'MFTMusclesArticle',
3
Added:
#superclass : 'MFTArticle',
4
Added:
#instVars : [
5
Added:
'muscleGroups'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components',
8
Added:
#package : 'MyFitnessTracker-Components'
9
Added:
}
10
Added:
11
Added:
{ #category : 'hooks' }
12
Added:
MFTMusclesArticle >> children [
13
Added:
14
Added:
^ muscleGroups
15
Added:
]
16
Added:
17
Added:
{ #category : 'initialization' }
18
Added:
MFTMusclesArticle >> initialize [
19
Added:
20
Added:
super initialize.
21
Added:
title := 'muscles'
22
Added:
]
23
Added:
24
Added:
{ #category : 'rendering' }
25
Added:
MFTMusclesArticle >> renderContentOn: html [
26
Added:
27
Added:
self renderArticleOn: html with: [
28
Added:
MFTSQLite3Database allMuscleGroups do: [ :muscleGroup |
29
Added:
html render: muscleGroup ] ]
30
Added:
]
src/MyFitnessTracker-Components/MFTPage.class.st
@@ -0,0 +1,24 @@
1
Added:
Class {
2
Added:
#name : 'MFTPage',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'title'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Pages',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Pages'
10
Added:
}
11
Added:
12
Added:
{ #category : 'rendering' }
13
Added:
MFTPage >> renderPageOn: html with: aBlock [
14
Added:
15
Added:
html container
16
Added:
style: 'max-width: 50rem';
17
Added:
with: aBlock
18
Added:
]
19
Added:
20
Added:
{ #category : 'accessing' }
21
Added:
MFTPage >> title [
22
Added:
23
Added:
^ title
24
Added:
]
src/MyFitnessTracker-Components/MFTPrimaryMuscleGroupDialog.class.st
@@ -0,0 +1,64 @@
1
Added:
Class {
2
Added:
#name : 'MFTPrimaryMuscleGroupDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'primaryMuscleGroup'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'initialization' }
13
Added:
MFTPrimaryMuscleGroupDialog >> initialize [
14
Added:
15
Added:
super initialize.
16
Added:
primaryMuscleGroup := MFTMuscleGroup new.
17
Added:
18
Added:
]
19
Added:
20
Added:
{ #category : 'accessing' }
21
Added:
MFTPrimaryMuscleGroupDialog >> primaryMuscleGroup [
22
Added:
23
Added:
^ primaryMuscleGroup
24
Added:
]
25
Added:
26
Added:
{ #category : 'accessing' }
27
Added:
MFTPrimaryMuscleGroupDialog >> primaryMuscleGroup: anObject [
28
Added:
29
Added:
primaryMuscleGroup := anObject
30
Added:
]
31
Added:
32
Added:
{ #category : 'rendering' }
33
Added:
MFTPrimaryMuscleGroupDialog >> renderContentOn: html [
34
Added:
35
Added:
| allMuscleGroups selectedMuscleGroup |
36
Added:
allMuscleGroups := MFTSQLite3Database allMuscleGroups.
37
Added:
selectedMuscleGroup := primaryMuscleGroup ifNotNil: [
38
Added:
allMuscleGroups detect: [ :muscleGroup |
39
Added:
muscleGroup englishName
40
Added:
= primaryMuscleGroup englishName ] ].
41
Added:
self renderDialogOn: html with: [
42
Added:
html formGroup: [
43
Added:
html select formControl
44
Added:
list: allMuscleGroups;
45
Added:
selected: selectedMuscleGroup;
46
Added:
labels: [ :muscleGroup | muscleGroup englishName ];
47
Added:
callback: [ :group | primaryMuscleGroup := group ] ] ]
48
Added:
]
49
Added:
50
Added:
{ #category : 'rendering' }
51
Added:
MFTPrimaryMuscleGroupDialog >> renderSubmitOn: html [
52
Added:
53
Added:
html buttonGroup: [
54
Added:
html formButton
55
Added:
beSubmit;
56
Added:
bePrimary;
57
Added:
callback: [ self answer: primaryMuscleGroup ];
58
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
59
Added:
html formButton
60
Added:
beSubmit;
61
Added:
beSecondary;
62
Added:
callback: [ self answer: nil ];
63
Added:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
64
Added:
]
src/MyFitnessTracker-Components/MFTPrimaryMuscleGroupFormComponent.class.st
@@ -0,0 +1,32 @@
1
Added:
Class {
2
Added:
#name : 'MFTPrimaryMuscleGroupFormComponent',
3
Added:
#superclass : 'MFTFormComponent',
4
Added:
#category : 'MyFitnessTracker-Components-Forms',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Forms'
7
Added:
}
8
Added:
9
Added:
{ #category : 'initialization' }
10
Added:
MFTPrimaryMuscleGroupFormComponent >> initialize [
11
Added:
12
Added:
super initialize.
13
Added:
heading := 'Primary Group'
14
Added:
]
15
Added:
16
Added:
{ #category : 'rendering' }
17
Added:
MFTPrimaryMuscleGroupFormComponent >> renderContentOn: html [
18
Added:
19
Added:
| allMuscleGroups selectedMuscleGroup |
20
Added:
allMuscleGroups := MFTSQLite3Database allMuscleGroups.
21
Added:
selectedMuscleGroup := modelObject primaryMuscleGroup ifNotNil: [
22
Added:
allMuscleGroups detect: [ :muscleGroup |
23
Added:
muscleGroup englishName
24
Added:
= modelObject primaryMuscleGroup englishName ] ].
25
Added:
26
Added:
self renderFormComponentOn: html with: [
27
Added:
html select formControl
28
Added:
list: allMuscleGroups;
29
Added:
selected: selectedMuscleGroup;
30
Added:
labels: [ :muscleGroup | muscleGroup englishName ];
31
Added:
callback: [ :group | modelObject primaryMuscleGroup: group ] ]
32
Added:
]
src/MyFitnessTracker-Components/MFTProfilePage.class.st
@@ -0,0 +1,50 @@
1
Added:
Class {
2
Added:
#name : 'MFTProfilePage',
3
Added:
#superclass : 'MFTPage',
4
Added:
#category : 'MyFitnessTracker-Components-Pages',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Pages'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTProfilePage >> editProfile [
11
Added:
12
Added:
| updatedUser |
13
Added:
updatedUser := self call: (MFTUserTask edit: self session user).
14
Added:
updatedUser ifNil: [ ^ self ].
15
Added:
self session user: updatedUser.
16
Added:
self session user isLoggedIn ifTrue: [
17
Added:
self session db updateUserWith: updatedUser ]
18
Added:
]
19
Added:
20
Added:
{ #category : 'initialization' }
21
Added:
MFTProfilePage >> initialize [
22
Added:
23
Added:
super initialize.
24
Added:
title := 'profile'
25
Added:
]
26
Added:
27
Added:
{ #category : 'rendering' }
28
Added:
MFTProfilePage >> renderContentOn: html [
29
Added:
30
Added:
| user |
31
Added:
user := self session user.
32
Added:
33
Added:
self renderPageOn: html with: [
34
Added:
html heading level1 with: [
35
Added:
html
36
Added:
code: self session user username;
37
Added:
text: '''s profile' ].
38
Added:
self renderProfileActionsOn: html.
39
Added:
html render: user ]
40
Added:
]
41
Added:
42
Added:
{ #category : 'rendering' }
43
Added:
MFTProfilePage >> renderProfileActionsOn: html [
44
Added:
45
Added:
html form: [
46
Added:
html formButton
47
Added:
bePrimary;
48
Added:
callback: [ self editProfile ];
49
Added:
with: 'Edit profile' ]
50
Added:
]
src/MyFitnessTracker-Components/MFTPromisesCard.class.st
@@ -0,0 +1,30 @@
1
Added:
Class {
2
Added:
#name : 'MFTPromisesCard',
3
Added:
#superclass : 'MFTCard',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'rendering' }
10
Added:
MFTPromisesCard >> renderContentOn: html [
11
Added:
12
Added:
self renderCardOn: html with: [
13
Added:
html cardHeader: 'My promises to all users:'.
14
Added:
html cardBody: [
15
Added:
html listGroup: [
16
Added:
html
17
Added:
listGroupItem:
18
Added:
'Every single feature shall always be free of charge.';
19
Added:
listGroupItem:
20
Added:
'No personal user data shall ever be sold, under any circumstances.';
21
Added:
listGroupItem:
22
Added:
'No external advertisements shall ever be displayed.' ].
23
Added:
html form
24
Added:
class: 'mt-3';
25
Added:
with: [
26
Added:
html outlineButton
27
Added:
bePrimary;
28
Added:
callback: [ self session pageManager setActivePageAt: #about ];
29
Added:
with: 'Why did I create this web app?' ] ] ]
30
Added:
]
src/MyFitnessTracker-Components/MFTPublicLandingPage.class.st
@@ -0,0 +1,17 @@
1
Added:
Class {
2
Added:
#name : 'MFTPublicLandingPage',
3
Added:
#superclass : 'MFTLandingPage',
4
Added:
#category : 'MyFitnessTracker-Components-Pages',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Pages'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTPublicLandingPage >> defaultCards [
11
Added:
12
Added:
^ {
13
Added:
MFTMainPublicCard new.
14
Added:
MFTWorkoutsCard new.
15
Added:
MFTWeightsCard new.
16
Added:
MFTPromisesCard new }
17
Added:
]
src/MyFitnessTracker-Components/MFTRegisterProspectEmailHeader.class.st
@@ -0,0 +1,137 @@
1
Added:
Class {
2
Added:
#name : 'MFTRegisterProspectEmailHeader',
3
Added:
#superclass : 'MFTHeaderComponent',
4
Added:
#instVars : [
5
Added:
'email',
6
Added:
'localPart',
7
Added:
'domain',
8
Added:
'isDismissed'
9
Added:
],
10
Added:
#category : 'MyFitnessTracker-Components',
11
Added:
#package : 'MyFitnessTracker-Components'
12
Added:
}
13
Added:
14
Added:
{ #category : 'adding' }
15
Added:
MFTRegisterProspectEmailHeader >> addUserEmail [
16
Added:
17
Added:
| emailToCheck emailPattern |
18
Added:
emailToCheck := self call: MFTEmailSignupDialog new.
19
Added:
emailPattern := '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]{3,}\.[a-zA-Z]{2,}$'
20
Added:
asRegex.
21
Added:
(emailToCheck isNil or: [ (emailPattern matches: emailToCheck) not ])
22
Added:
ifTrue: [ ^ nil ].
23
Added:
email := emailToCheck.
24
Added:
MFTSQLite3Database storeProspectEmail: email
25
Added:
]
26
Added:
27
Added:
{ #category : 'initialization' }
28
Added:
MFTRegisterProspectEmailHeader >> initialize [
29
Added:
30
Added:
super initialize.
31
Added:
isDismissed := false
32
Added:
]
33
Added:
34
Added:
{ #category : 'accessing' }
35
Added:
MFTRegisterProspectEmailHeader >> isDismissed [
36
Added:
37
Added:
^ isDismissed
38
Added:
]
39
Added:
40
Added:
{ #category : 'accessing' }
41
Added:
MFTRegisterProspectEmailHeader >> isDismissed: anObject [
42
Added:
43
Added:
isDismissed := anObject
44
Added:
]
45
Added:
46
Added:
{ #category : 'rendering' }
47
Added:
MFTRegisterProspectEmailHeader >> renderContentOn: html [
48
Added:
49
Added:
isDismissed ifTrue: [ ^ self ].
50
Added:
email
51
Added:
ifNil: [
52
Added:
html alert
53
Added:
beWarning;
54
Added:
marginBottom: 0;
55
Added:
with: [
56
Added:
html alertHeading: 'This app is still in beta.'.
57
Added:
html paragraph: [
58
Added:
html
59
Added:
text:
60
Added:
'My Fitness Tracker is still under heavy development, with improvements being made daily.';
61
Added:
space;
62
Added:
text: 'I can''t wait to share the finished product with you!' ].
63
Added:
html form with: [
64
Added:
html label
65
Added:
style: 'margin-right: 1em';
66
Added:
with: [ html strong: 'Want to know when version 1.0 is out?' ].
67
Added:
html buttonGroup: [
68
Added:
html formButton
69
Added:
beInfo;
70
Added:
callback: [ self addUserEmail ];
71
Added:
with: 'E-mail me!'.
72
Added:
html formButton
73
Added:
beSecondary;
74
Added:
callback: [ isDismissed := true ];
75
Added:
with: 'I''m not interested' ] ] ] ]
76
Added:
ifNotNil: [
77
Added:
html alert
78
Added:
beSuccess;
79
Added:
beDismissible;
80
Added:
marginBottom: 0;
81
Added:
with: [
82
Added:
html closeButton dataDismiss: 'alert'.
83
Added:
html
84
Added:
text: 'You''ll be notified at ';
85
Added:
strong: email;
86
Added:
text: ' when My Fitness Tracker is ready for you!' ] ]
87
Added:
]
88
Added:
89
Added:
{ #category : 'rendering' }
90
Added:
MFTRegisterProspectEmailHeader >> renderEmailInputModalButtonOn: html [
91
Added:
92
Added:
html formButton
93
Added:
bePrimary;
94
Added:
beSmall;
95
Added:
dataToggle: 'modal';
96
Added:
dataTarget: '#emailInputModal';
97
Added:
with: 'E-mail me!'
98
Added:
]
99
Added:
100
Added:
{ #category : 'rendering' }
101
Added:
MFTRegisterProspectEmailHeader >> renderEmailInputModalOn: html [
102
Added:
103
Added:
html modal
104
Added:
id: 'emailInputModal';
105
Added:
fade;
106
Added:
with: [
107
Added:
html modalDialog
108
Added:
beCentered;
109
Added:
with: [
110
Added:
html modalContent: [
111
Added:
html form: [
112
Added:
html modalHeader: [
113
Added:
html modalTitle
114
Added:
level5;
115
Added:
with:
116
Added:
'You''ll be the first to know when My Fitness Tracker is ready to use.'.
117
Added:
html modalCloseButton ].
118
Added:
119
Added:
html modalBody: [
120
Added:
html inputGroup: [
121
Added:
html formTextInput
122
Added:
callback: [ :value | localPart := value ];
123
Added:
placeholder: 'my-user'.
124
Added:
html inputGroupAppend: [ html inputGroupText: '@' ].
125
Added:
html formTextInput
126
Added:
callback: [ :value | domain := value ];
127
Added:
placeholder: 'example.com' ] ].
128
Added:
129
Added:
html modalFooter: [
130
Added:
html paragraph:
131
Added:
'You will receive a single e-mail when the app is ready to use. Your e-mail address shall not be used in any other way.'.
132
Added:
html formButton
133
Added:
bePrimary;
134
Added:
beSubmit;
135
Added:
callback: [ email := localPart , '@' , domain ];
136
Added:
with: 'Confirm' ] ] ] ] ]
137
Added:
]
src/MyFitnessTracker-Components/MFTRepsDistributionGraph.class.st
@@ -0,0 +1,54 @@
1
Added:
Class {
2
Added:
#name : 'MFTRepsDistributionGraph',
3
Added:
#superclass : 'MFTGraph',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTRepsDistributionGraph >> crunchTheNumbers [
11
Added:
"TODO: This looks ugly."
12
Added:
13
Added:
| sets repTally |
14
Added:
sets := self session user workouts flatCollect: #sets.
15
Added:
repTally := Dictionary new.
16
Added:
sets do: [ :set |
17
Added:
repTally at: set exercise englishName ifAbsentPut: [ 0 ].
18
Added:
repTally
19
Added:
at: set exercise englishName
20
Added:
put: (repTally at: set exercise englishName) + set reps ].
21
Added:
22
Added:
labels := repTally keys.
23
Added:
data := repTally values
24
Added:
]
25
Added:
26
Added:
{ #category : 'initialization' }
27
Added:
MFTRepsDistributionGraph >> initialize [
28
Added:
29
Added:
super initialize.
30
Added:
self crunchTheNumbers
31
Added:
]
32
Added:
33
Added:
{ #category : 'rendering' }
34
Added:
MFTRepsDistributionGraph >> renderContentOn: html [
35
Added:
36
Added:
html canvas id: 'repsVsTimeChart'.
37
Added:
html script:
38
Added:
'var ctx = document.getElementById("repsVsTimeChart").getContext("2d");
39
Added:
var workoutChart = new Chart(ctx, {
40
Added:
type: "doughnut",
41
Added:
data: {
42
Added:
labels: ' , labels asJavascript , ',
43
Added:
datasets: [{
44
Added:
data: ' , data asJavascript , '
45
Added:
}]
46
Added:
},
47
Added:
options: {
48
Added:
title: {
49
Added:
display: true,
50
Added:
text: "Reps by exercise"
51
Added:
}
52
Added:
}
53
Added:
});'
54
Added:
]
src/MyFitnessTracker-Components/MFTRootComponent.class.st
@@ -0,0 +1,101 @@
1
Added:
Class {
2
Added:
#name : 'MFTRootComponent',
3
Added:
#superclass : 'SBSRootComponent',
4
Added:
#instVars : [
5
Added:
'main'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components',
8
Added:
#package : 'MyFitnessTracker-Components'
9
Added:
}
10
Added:
11
Added:
{ #category : 'testing' }
12
Added:
MFTRootComponent class >> canBeRoot [
13
Added:
14
Added:
^ true
15
Added:
]
16
Added:
17
Added:
{ #category : 'class initialization' }
18
Added:
MFTRootComponent class >> initialize [
19
Added:
20
Added:
| application libraries |
21
Added:
application := WAAdmin register: self asApplicationAt: 'mft'.
22
Added:
application preferenceAt: #sessionClass put: MFTSession.
23
Added:
libraries := MFTUtils isDevelopmentImage
24
Added:
ifTrue: [ self libraries at: #development ]
25
Added:
ifFalse: [ self libraries at: #deployment ].
26
Added:
libraries do: [ :library | application addLibrary: library ].
27
Added:
^ application
28
Added:
]
29
Added:
30
Added:
{ #category : 'class initialization' }
31
Added:
MFTRootComponent class >> libraries [
32
Added:
33
Added:
^ Dictionary new
34
Added:
at: #development put: {
35
Added:
JQDevelopmentLibrary.
36
Added:
JQUiDevelopmentLibrary.
37
Added:
SBSDevelopmentLibrary };
38
Added:
at: #deployment put: {
39
Added:
JQGoogleLibrary.
40
Added:
JQUiGoogleLibrary.
41
Added:
SBSCDNDeploymentLibrary };
42
Added:
yourself
43
Added:
]
44
Added:
45
Added:
{ #category : 'adding' }
46
Added:
MFTRootComponent >> addBootstrapJSScriptTo: html [
47
Added:
48
Added:
| library |
49
Added:
library := MFTUtils isDevelopmentImage
50
Added:
ifTrue: [ SBSDeploymentLibrary ]
51
Added:
ifFalse: [ SBSCDNDeploymentLibrary ].
52
Added:
library addLoadScriptTo: html
53
Added:
]
54
Added:
55
Added:
{ #category : 'rendering' }
56
Added:
MFTRootComponent >> children [
57
Added:
58
Added:
^ { main }
59
Added:
]
60
Added:
61
Added:
{ #category : 'initialization' }
62
Added:
MFTRootComponent >> initialize [
63
Added:
64
Added:
super initialize.
65
Added:
main := MFTScreenComponent new
66
Added:
]
67
Added:
68
Added:
{ #category : 'initialization' }
69
Added:
MFTRootComponent >> main: aComponent [
70
Added:
71
Added:
main := aComponent
72
Added:
]
73
Added:
74
Added:
{ #category : 'rendering' }
75
Added:
MFTRootComponent >> renderContentOn: html [
76
Added:
77
Added:
html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
78
Added:
html render: main.
79
Added:
self addBootstrapJSScriptTo: html
80
Added:
]
81
Added:
82
Added:
{ #category : 'updating' }
83
Added:
MFTRootComponent >> updateRoot: anHtmlRoot [
84
Added:
85
Added:
super updateRoot: anHtmlRoot.
86
Added:
anHtmlRoot beHtml5.
87
Added:
anHtmlRoot meta
88
Added:
name: 'viewport';
89
Added:
content: 'width=device-width, initial-scale=1'.
90
Added:
anHtmlRoot title: 'My Fitness Tracker ' , self version.
91
Added:
anHtmlRoot javascript url: 'https://cdn.jsdelivr.net/npm/chart.js'.
92
Added:
anHtmlRoot link
93
Added:
beShortcutIcon;
94
Added:
url: MFTFileLibrary / #faviconPng
95
Added:
]
96
Added:
97
Added:
{ #category : 'versions' }
98
Added:
MFTRootComponent >> version [
99
Added:
100
Added:
^ MFTUtils version ifNil: [ '[DEVELOP]' ]
101
Added:
]
src/MyFitnessTracker-Components/MFTSandboxPage.class.st
@@ -0,0 +1,22 @@
1
Added:
Class {
2
Added:
#name : 'MFTSandboxPage',
3
Added:
#superclass : 'MFTPage',
4
Added:
#category : 'MyFitnessTracker-Components-Pages',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Pages'
7
Added:
}
8
Added:
9
Added:
{ #category : 'initialization' }
10
Added:
MFTSandboxPage >> initialize [
11
Added:
12
Added:
super initialize.
13
Added:
title := 'sandbox'
14
Added:
]
15
Added:
16
Added:
{ #category : 'rendering' }
17
Added:
MFTSandboxPage >> renderContentOn: html [
18
Added:
19
Added:
self renderPageOn: html with: [
20
Added:
html text: 'Welcome to the sandbox page.'.
21
Added:
MFTSQLite3Database allMuscleGroups do: [ :each | html render: each ] ]
22
Added:
]
src/MyFitnessTracker-Components/MFTScreenComponent.class.st
@@ -0,0 +1,38 @@
1
Added:
Class {
2
Added:
#name : 'MFTScreenComponent',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'pm'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components',
8
Added:
#package : 'MyFitnessTracker-Components'
9
Added:
}
10
Added:
11
Added:
{ #category : 'hooks' }
12
Added:
MFTScreenComponent >> children [
13
Added:
14
Added:
^ {
15
Added:
pm header.
16
Added:
pm mainNavigationBar.
17
Added:
pm activePage.
18
Added:
pm footer }
19
Added:
]
20
Added:
21
Added:
{ #category : 'initialization' }
22
Added:
MFTScreenComponent >> initialize [
23
Added:
24
Added:
super initialize.
25
Added:
pm := self session pageManager
26
Added:
]
27
Added:
28
Added:
{ #category : 'rendering' }
29
Added:
MFTScreenComponent >> renderContentOn: html [
30
Added:
31
Added:
(self session user isLoggedIn not and: [
32
Added:
MFTUtils isDevelopmentImage not ]) ifTrue: [
33
Added:
html render: pm header ].
34
Added:
html
35
Added:
render: pm mainNavigationBar;
36
Added:
render: pm activePage;
37
Added:
render: pm footer
38
Added:
]
src/MyFitnessTracker-Components/MFTSecondaryMuscleGroupsDialog.class.st
@@ -0,0 +1,65 @@
1
Added:
Class {
2
Added:
#name : 'MFTSecondaryMuscleGroupsDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'secondaryMuscleGroups'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'initialization' }
13
Added:
MFTSecondaryMuscleGroupsDialog >> initialize [
14
Added:
15
Added:
super initialize.
16
Added:
secondaryMuscleGroups := OrderedCollection new
17
Added:
]
18
Added:
19
Added:
{ #category : 'rendering' }
20
Added:
MFTSecondaryMuscleGroupsDialog >> renderContentOn: html [
21
Added:
22
Added:
| allMuscleGroups selectedMuscles |
23
Added:
allMuscleGroups := MFTSQLite3Database allMuscleGroups.
24
Added:
"selectedMuscles := allMuscles
25
Added:
detect: [ :each |
26
Added:
each englishName
27
Added:
= (secondaryMuscles collect: #englishName) ]
28
Added:
ifNone: [ nil ]."
29
Added:
30
Added:
self renderDialogOn: html with: [
31
Added:
html multiSelect formControl
32
Added:
list: allMuscleGroups;
33
Added:
"selected: selectedMuscles;"labels: [ :muscleGroup |
34
Added:
muscleGroup englishName ];
35
Added:
callback: [ :groups |
36
Added:
groups ifNotEmpty: [ secondaryMuscleGroups := groups ] ] ]
37
Added:
]
38
Added:
39
Added:
{ #category : 'rendering' }
40
Added:
MFTSecondaryMuscleGroupsDialog >> renderSubmitOn: html [
41
Added:
42
Added:
html buttonGroup: [
43
Added:
html formButton
44
Added:
beSubmit;
45
Added:
bePrimary;
46
Added:
callback: [ self answer: secondaryMuscleGroups ];
47
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
48
Added:
html formButton
49
Added:
beSubmit;
50
Added:
beSecondary;
51
Added:
callback: [ self answer: OrderedCollection new ];
52
Added:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
53
Added:
]
54
Added:
55
Added:
{ #category : 'accessing' }
56
Added:
MFTSecondaryMuscleGroupsDialog >> secondaryMuscleGroups [
57
Added:
58
Added:
^ secondaryMuscleGroups
59
Added:
]
60
Added:
61
Added:
{ #category : 'accessing' }
62
Added:
MFTSecondaryMuscleGroupsDialog >> secondaryMuscleGroups: anObject [
63
Added:
64
Added:
secondaryMuscleGroups := anObject
65
Added:
]
src/MyFitnessTracker-Components/MFTSecondaryMuscleGroupsFormComponent.class.st
@@ -0,0 +1,40 @@
1
Added:
Class {
2
Added:
#name : 'MFTSecondaryMuscleGroupsFormComponent',
3
Added:
#superclass : 'MFTFormComponent',
4
Added:
#instVars : [
5
Added:
'allMuscleGroups'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Forms',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Forms'
10
Added:
}
11
Added:
12
Added:
{ #category : 'as yet unclassified' }
13
Added:
MFTSecondaryMuscleGroupsFormComponent >> getSelectedMuscleGroups [
14
Added:
15
Added:
| selectedMuscleGroups |
16
Added:
selectedMuscleGroups := allMuscleGroups select: [ :muscleGroup |
17
Added:
modelObject secondaryMuscleGroupIds
18
Added:
includes: muscleGroup id ].
19
Added:
^ selectedMuscleGroups
20
Added:
]
21
Added:
22
Added:
{ #category : 'initialization' }
23
Added:
MFTSecondaryMuscleGroupsFormComponent >> initialize [
24
Added:
25
Added:
super initialize.
26
Added:
heading := 'Secondary Groups'.
27
Added:
allMuscleGroups := MFTSQLite3Database allMuscleGroups
28
Added:
]
29
Added:
30
Added:
{ #category : 'rendering' }
31
Added:
MFTSecondaryMuscleGroupsFormComponent >> renderContentOn: html [
32
Added:
33
Added:
self renderFormComponentOn: html with: [
34
Added:
html multiSelect formControl
35
Added:
list: allMuscleGroups;
36
Added:
selected: self getSelectedMuscleGroups;
37
Added:
labels: [ :muscleGroup | muscleGroup englishName ];
38
Added:
callback: [ :muscleGroups |
39
Added:
modelObject secondaryMuscleGroups: muscleGroups ] ]
40
Added:
]
src/MyFitnessTracker-Components/MFTSetDialog.class.st
@@ -0,0 +1,10 @@
1
Added:
Class {
2
Added:
#name : 'MFTSetDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'set'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
src/MyFitnessTracker-Components/MFTSetGroupDialog.class.st
@@ -0,0 +1,319 @@
1
Added:
Class {
2
Added:
#name : 'MFTSetGroupDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'allExercises',
6
Added:
'selectedExercises',
7
Added:
'setGroup'
8
Added:
],
9
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
10
Added:
#package : 'MyFitnessTracker-Components',
11
Added:
#tag : 'Dialogs'
12
Added:
}
13
Added:
14
Added:
{ #category : 'as yet unclassified' }
15
Added:
MFTSetGroupDialog >> addSet [
16
Added:
17
Added:
| set |
18
Added:
set := self call:
19
Added:
(MFTSetDialog newForExercise: selectedExercises first).
20
Added:
set ifNil: [ ^ self ].
21
Added:
setGroup sets ifEmpty: [ setGroup := MFTSetGroup new ].
22
Added:
setGroup addSet: (set exercise: selectedExercises first)
23
Added:
]
24
Added:
25
Added:
{ #category : 'as yet unclassified' }
26
Added:
MFTSetGroupDialog >> addSets: aMFTExerciseCollection [
27
Added:
28
Added:
| sets |
29
Added:
sets := self call:
30
Added:
((MFTSetsDialog newForExercises:
31
Added:
aMFTExerciseCollection) subheading:
32
Added:
((selectedExercises collect: #englishName) joinUsing: '/')).
33
Added:
sets ifNotEmpty: [ setGroup addSets: sets ]
34
Added:
]
35
Added:
36
Added:
{ #category : 'as yet unclassified' }
37
Added:
MFTSetGroupDialog >> editSet: aMFTSet [
38
Added:
39
Added:
| oldSet newSet |
40
Added:
oldSet := aMFTSet.
41
Added:
newSet := self call:
42
Added:
(MFTSetDialog
43
Added:
edit: oldSet copy
44
Added:
withExercise: selectedExercises first).
45
Added:
newSet ifNotNil: [ setGroup updateSet: oldSet with: newSet ]
46
Added:
]
47
Added:
48
Added:
{ #category : 'as yet unclassified' }
49
Added:
MFTSetGroupDialog >> editSets: aMFTSetCollection [
50
Added:
51
Added:
| oldSets newSets |
52
Added:
oldSets := aMFTSetCollection.
53
Added:
newSets := self call:
54
Added:
(MFTSetsDialog
55
Added:
edit: oldSets copy
56
Added:
withExercises: selectedExercises).
57
Added:
newSets ifNotEmpty: [ setGroup updateSets: oldSets with: newSets ]
58
Added:
]
59
Added:
60
Added:
{ #category : 'initialization' }
61
Added:
MFTSetGroupDialog >> initialize [
62
Added:
63
Added:
super initialize.
64
Added:
setGroup := MFTSetGroup new sets:
65
Added:
(OrderedCollection with: (MFTSet new reps: 8)).
66
Added:
allExercises := MFTSQLite3Database allExercises.
67
Added:
selectedExercises := OrderedCollection with: allExercises first
68
Added:
]
69
Added:
70
Added:
{ #category : 'as yet unclassified' }
71
Added:
MFTSetGroupDialog >> makeGiantSet [
72
Added:
73
Added:
self heading: 'Giant Sets'.
74
Added:
selectedExercises := OrderedCollection withAll:
75
Added:
(allExercises copyFrom: 1 to: 5).
76
Added:
self populateSetsForExercises: selectedExercises
77
Added:
]
78
Added:
79
Added:
{ #category : 'as yet unclassified' }
80
Added:
MFTSetGroupDialog >> makeRegularExercise [
81
Added:
82
Added:
self heading: 'Sets'.
83
Added:
selectedExercises := OrderedCollection with: allExercises first.
84
Added:
self populateSetsForExercises: selectedExercises
85
Added:
]
86
Added:
87
Added:
{ #category : 'as yet unclassified' }
88
Added:
MFTSetGroupDialog >> makeSuperset [
89
Added:
90
Added:
self heading: 'Supersets'.
91
Added:
selectedExercises := OrderedCollection
92
Added:
with: allExercises first
93
Added:
with: allExercises second.
94
Added:
self populateSetsForExercises: selectedExercises
95
Added:
]
96
Added:
97
Added:
{ #category : 'as yet unclassified' }
98
Added:
MFTSetGroupDialog >> makeTriset [
99
Added:
100
Added:
self heading: 'Tri-sets'.
101
Added:
selectedExercises := OrderedCollection
102
Added:
with: allExercises first
103
Added:
with: allExercises second
104
Added:
with: allExercises third.
105
Added:
self populateSetsForExercises: selectedExercises
106
Added:
]
107
Added:
108
Added:
{ #category : 'as yet unclassified' }
109
Added:
MFTSetGroupDialog >> populateSetsForExercises: aMFTExerciseCollection [
110
Added:
111
Added:
setGroup := MFTSetGroup new sets:
112
Added:
(aMFTExerciseCollection collect: [ :exercise |
113
Added:
MFTSet new
114
Added:
reps: 8;
115
Added:
exercise: exercise ])
116
Added:
]
117
Added:
118
Added:
{ #category : 'as yet unclassified' }
119
Added:
MFTSetGroupDialog >> renderConfigureSetGroupButtonsOn: html [
120
Added:
121
Added:
selectedExercises size = 1
122
Added:
ifTrue: [
123
Added:
html outlineButton bePrimary beSmall
124
Added:
class: 'me-2';
125
Added:
callback: [ self addSet ];
126
Added:
with: 'Add set' ]
127
Added:
ifFalse: [
128
Added:
html outlineButton bePrimary beSmall
129
Added:
class: 'me-2';
130
Added:
callback: [ self addSets: selectedExercises ];
131
Added:
with: 'Add set group' ].
132
Added:
html buttonGroup: [
133
Added:
selectedExercises size ~~ 1 ifTrue: [
134
Added:
html outlineButton beSecondary beSmall
135
Added:
callback: [ self makeRegularExercise ];
136
Added:
with: 'Regular exercise' ].
137
Added:
selectedExercises size ~~ 2 ifTrue: [
138
Added:
html outlineButton beSecondary beSmall
139
Added:
callback: [ self makeSuperset ];
140
Added:
with: 'Superset' ].
141
Added:
selectedExercises size ~~ 3 ifTrue: [
142
Added:
html outlineButton beSecondary beSmall
143
Added:
callback: [ self makeTriset ];
144
Added:
with: 'Tri-set' ].
145
Added:
selectedExercises size < 4 ifTrue: [
146
Added:
html outlineButton beSecondary beSmall
147
Added:
callback: [ self makeGiantSet ];
148
Added:
with: 'Giant set' ] ]
149
Added:
]
150
Added:
151
Added:
{ #category : 'rendering' }
152
Added:
MFTSetGroupDialog >> renderContentOn: html [
153
Added:
154
Added:
self renderDialogOn: html with: [
155
Added:
self renderSelectExercisesOn: html.
156
Added:
html horizontalRule.
157
Added:
setGroup sets ifNotEmpty: [ self renderSetGroupTableOn: html ].
158
Added:
self renderConfigureSetGroupButtonsOn: html ]
159
Added:
]
160
Added:
161
Added:
{ #category : 'as yet unclassified' }
162
Added:
MFTSetGroupDialog >> renderSelectExercisesOn: html [
163
Added:
164
Added:
| allSets |
165
Added:
allSets := setGroup sets.
166
Added:
selectedExercises doWithIndex: [ :selectedExercise :i |
167
Added:
html formGroup: [
168
Added:
html label: 'Exercise ' , (selectedExercises size > 1
169
Added:
ifTrue: [ i asString ]
170
Added:
ifFalse: [ '' ]).
171
Added:
html select formControl
172
Added:
list: allExercises;
173
Added:
selected: selectedExercise;
174
Added:
callback: [ :exercise |
175
Added:
selectedExercises at: i put: exercise.
176
Added:
self updateAllSelectedExercises ];
177
Added:
labels: [ :each | each englishName ] ] ]
178
Added:
]
179
Added:
180
Added:
{ #category : 'rendering' }
181
Added:
MFTSetGroupDialog >> renderSetGroupRowsOn: html [
182
Added:
183
Added:
setGroup sets doWithIndex: [ :set :i |
184
Added:
(i - 1 isDivisibleBy: selectedExercises size) ifTrue: [
185
Added:
| selectedSets |
186
Added:
selectedSets := setGroup sets atAll:
187
Added:
(i to: i + selectedExercises size - 1).
188
Added:
html tableRow with: [
189
Added:
html tableData
190
Added:
attributeAt: 'rowspan' put: selectedExercises size + 1;
191
Added:
style: 'vertical-align: middle';
192
Added:
with: [
193
Added:
html buttonGroup: [
194
Added:
html outlineButton
195
Added:
beDanger;
196
Added:
beSmall;
197
Added:
callback: [ setGroup removeSets: selectedSets ];
198
Added:
with: 'Remove'.
199
Added:
html outlineButton
200
Added:
beSecondary;
201
Added:
beSmall;
202
Added:
callback: [
203
Added:
selectedSets size = 1
204
Added:
ifTrue: [ self editSet: selectedSets first ]
205
Added:
ifFalse: [ self editSets: selectedSets ] ];
206
Added:
with: 'Edit'.
207
Added:
selectedExercises size = 1 ifTrue: [
208
Added:
html outlineButton
209
Added:
beSecondary;
210
Added:
beSmall;
211
Added:
callback: [ setGroup duplicateSet: selectedSets first ];
212
Added:
with: 'Duplicate' ] ] ] ] ].
213
Added:
214
Added:
html tableRow: [ "This row spans the amount of sets for a given exercise."
215
Added:
setGroup isIrregularSetGroup ifTrue: [
216
Added:
html tableData with: set exercise englishName capitalized ].
217
Added:
html tableData
218
Added:
style: 'text-align: right';
219
Added:
with: set reps.
220
Added:
setGroup hasLoad ifTrue: [
221
Added:
html tableData
222
Added:
style: 'text-align: right';
223
Added:
with: set load ].
224
Added:
setGroup hasRpe ifTrue: [
225
Added:
html tableData
226
Added:
style: 'text-align: right';
227
Added:
with: set rpe ].
228
Added:
setGroup hasRestAfter ifTrue: [
229
Added:
html tableData
230
Added:
style: 'text-align: right';
231
Added:
with: set restAfter ] ] ]
232
Added:
]
233
Added:
234
Added:
{ #category : 'rendering' }
235
Added:
MFTSetGroupDialog >> renderSetGroupTableHeaderOn: html [
236
Added:
237
Added:
html tableHead: [
238
Added:
html tableHeading.
239
Added:
setGroup isIrregularSetGroup ifTrue: [
240
Added:
html tableHeading with: 'Exercise' ].
241
Added:
html tableHeading
242
Added:
style: 'text-align: right';
243
Added:
with: 'Reps'.
244
Added:
setGroup hasLoad ifTrue: [
245
Added:
html tableHeading
246
Added:
style: 'text-align: right';
247
Added:
with: 'Load' ].
248
Added:
setGroup hasRpe ifTrue: [
249
Added:
html tableHeading
250
Added:
style: 'text-align: right';
251
Added:
with: 'RPE' ].
252
Added:
setGroup hasRestAfter ifTrue: [
253
Added:
html tableHeading
254
Added:
style: 'text-align: right';
255
Added:
with: 'Rest After (s)' ] ]
256
Added:
]
257
Added:
258
Added:
{ #category : 'rendering' }
259
Added:
MFTSetGroupDialog >> renderSetGroupTableOn: html [
260
Added:
261
Added:
html table
262
Added:
class: 'table table-bordered';
263
Added:
with: [
264
Added:
self renderSetGroupTableHeaderOn: html.
265
Added:
self renderSetGroupRowsOn: html ]
266
Added:
]
267
Added:
268
Added:
{ #category : 'as yet unclassified' }
269
Added:
MFTSetGroupDialog >> renderSubmitOn: html [
270
Added:
271
Added:
html buttonGroup: [
272
Added:
html formButton
273
Added:
beSubmit;
274
Added:
bePrimary;
275
Added:
callback: [ self answer: setGroup ];
276
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
277
Added:
html formButton
278
Added:
beSubmit;
279
Added:
beSecondary;
280
Added:
callback: [ self answer: nil ];
281
Added:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
282
Added:
]
283
Added:
284
Added:
{ #category : 'as yet unclassified' }
285
Added:
MFTSetGroupDialog >> selectedExercises [
286
Added:
287
Added:
^ selectedExercises
288
Added:
]
289
Added:
290
Added:
{ #category : 'as yet unclassified' }
291
Added:
MFTSetGroupDialog >> selectedExercises: aMFTExerciseCollection [
292
Added:
293
Added:
selectedExercises := aMFTExerciseCollection
294
Added:
]
295
Added:
296
Added:
{ #category : 'as yet unclassified' }
297
Added:
MFTSetGroupDialog >> setGroup [
298
Added:
299
Added:
^ setGroup
300
Added:
]
301
Added:
302
Added:
{ #category : 'as yet unclassified' }
303
Added:
MFTSetGroupDialog >> setGroup: anObject [
304
Added:
305
Added:
setGroup := anObject
306
Added:
]
307
Added:
308
Added:
{ #category : 'as yet unclassified' }
309
Added:
MFTSetGroupDialog >> updateAllSelectedExercises [
310
Added:
311
Added:
| allSets |
312
Added:
allSets := setGroup sets.
313
Added:
selectedExercises doWithIndex: [ :selectedExercise :i |
314
Added:
i to: allSets size by: selectedExercises size do: [ :j |
315
Added:
| set |
316
Added:
"Update every nth set to reflect the newly selected exercise selectedExercises at: i."
317
Added:
set := allSets at: j.
318
Added:
set exercise: selectedExercise ] ]
319
Added:
]
src/MyFitnessTracker-Components/MFTSetGroupForm.class.st
@@ -0,0 +1,144 @@
1
Added:
Class {
2
Added:
#name : 'MFTSetGroupForm',
3
Added:
#superclass : 'MFTForm',
4
Added:
#instVars : [
5
Added:
'exerciseCount',
6
Added:
'renderRpe',
7
Added:
'renderRestAfter'
8
Added:
],
9
Added:
#category : 'MyFitnessTracker-Components-Forms',
10
Added:
#package : 'MyFitnessTracker-Components',
11
Added:
#tag : 'Forms'
12
Added:
}
13
Added:
14
Added:
{ #category : 'as yet unclassified' }
15
Added:
MFTSetGroupForm class >> forExercises: anMFTExerciseCollection [
16
Added:
17
Added:
| setGroup |
18
Added:
setGroup := MFTSetGroup new addSets:
19
Added:
(anMFTExerciseCollection collect: [ :exercise |
20
Added:
MFTSet new
21
Added:
exercise: exercise;
22
Added:
reps: 8 ]).
23
Added:
^ self new
24
Added:
modelObject: setGroup;
25
Added:
exerciseCount: anMFTExerciseCollection size;
26
Added:
yourself
27
Added:
]
28
Added:
29
Added:
{ #category : 'accessing' }
30
Added:
MFTSetGroupForm >> exerciseCount: anInteger [
31
Added:
32
Added:
exerciseCount := anInteger
33
Added:
]
34
Added:
35
Added:
{ #category : 'initialization' }
36
Added:
MFTSetGroupForm >> initialize [
37
Added:
38
Added:
super initialize.
39
Added:
modelObject := MFTSetGroup new addSet: (MFTSet new reps: 8).
40
Added:
heading := 'Sets'.
41
Added:
renderRpe := false.
42
Added:
renderRestAfter := false
43
Added:
]
44
Added:
45
Added:
{ #category : 'rendering' }
46
Added:
MFTSetGroupForm >> renderContentOn: html [
47
Added:
48
Added:
self renderFormOn: html with: [
49
Added:
self renderSetsButtonGroupOn: html.
50
Added:
self renderSetGroupTableOn: html ]
51
Added:
]
52
Added:
53
Added:
{ #category : 'rendering' }
54
Added:
MFTSetGroupForm >> renderSetGroupRowsOn: html [
55
Added:
56
Added:
| selectedExercises renderSetGroupExercisesName |
57
Added:
selectedExercises := modelObject sets collect: #exercise.
58
Added:
renderSetGroupExercisesName := modelObject isIrregularSetGroup.
59
Added:
60
Added:
modelObject sets do: [ :set |
61
Added:
html tableRow: [
62
Added:
renderSetGroupExercisesName ifTrue: [
63
Added:
html tableData with: set exercise englishName capitalized ].
64
Added:
html tableData: [
65
Added:
html numberInput formControl
66
Added:
value: set reps;
67
Added:
callback: [ :value | set reps: value asInteger ] ].
68
Added:
html tableData: [
69
Added:
html numberInput formControl
70
Added:
value: set load;
71
Added:
callback: [ :value |
72
Added:
set load:
73
Added:
(value ifNotEmpty: [ value asNumber ] ifEmpty: [ nil ]) ] ].
74
Added:
renderRpe ifTrue: [
75
Added:
html tableData: [
76
Added:
html numberInput formControl
77
Added:
value: set rpe;
78
Added:
callback: [ :value |
79
Added:
set rpe:
80
Added:
(value ifNotEmpty: [ value asInteger ] ifEmpty: [ nil ]) ] ] ].
81
Added:
renderRestAfter ifTrue: [
82
Added:
html tableData: [
83
Added:
html numberInput formControl
84
Added:
value: set restAfter;
85
Added:
callback: [ :value |
86
Added:
set restAfter:
87
Added:
(value ifNotEmpty: [ value asNumber ] ifEmpty: [ nil ]) ] ] ] ] ]
88
Added:
]
89
Added:
90
Added:
{ #category : 'rendering' }
91
Added:
MFTSetGroupForm >> renderSetGroupTableHeaderOn: html [
92
Added:
93
Added:
html tableHead: [
94
Added:
modelObject isIrregularSetGroup ifTrue: [
95
Added:
html tableHeading with: 'Exercise' ].
96
Added:
html tableHeading with: 'Reps'.
97
Added:
html tableHeading with: 'Load'.
98
Added:
renderRpe ifTrue: [ html tableHeading with: 'RPE' ].
99
Added:
renderRestAfter ifTrue: [ html tableHeading with: 'Rest After (s)' ] ]
100
Added:
]
101
Added:
102
Added:
{ #category : 'rendering' }
103
Added:
MFTSetGroupForm >> renderSetGroupTableOn: html [
104
Added:
105
Added:
html table
106
Added:
class: 'table table-bordered';
107
Added:
with: [
108
Added:
self renderSetGroupTableHeaderOn: html.
109
Added:
self renderSetGroupRowsOn: html ]
110
Added:
]
111
Added:
112
Added:
{ #category : 'rendering' }
113
Added:
MFTSetGroupForm >> renderSetsButtonGroupOn: html [
114
Added:
115
Added:
html buttonGroup
116
Added:
class: 'my-3';
117
Added:
with: [
118
Added:
html outlineButton
119
Added:
bePrimary;
120
Added:
beSmall;
121
Added:
callback: [ modelObject addSets: modelObject sets ];
122
Added:
with: 'Add sets'.
123
Added:
renderRpe ifFalse: [
124
Added:
html outlineButton
125
Added:
beSecondary;
126
Added:
beSmall;
127
Added:
callback: [ renderRpe := true ];
128
Added:
with: 'Add RPE' ].
129
Added:
renderRestAfter ifFalse: [
130
Added:
html outlineButton
131
Added:
beSecondary;
132
Added:
beSmall;
133
Added:
callback: [ renderRestAfter := true ];
134
Added:
with: 'Add rest' ].
135
Added:
html outlineButton
136
Added:
beDanger;
137
Added:
beSmall;
138
Added:
callback: [
139
Added:
| firstSets |
140
Added:
firstSets := modelObject sets first: exerciseCount.
141
Added:
modelObject := MFTSetGroup new addSets: firstSets.
142
Added:
renderRpe := renderRestAfter := false ];
143
Added:
with: 'Reset' ]
144
Added:
]
src/MyFitnessTracker-Components/MFTSetGroupTask.class.st
@@ -0,0 +1,63 @@
1
Added:
Class {
2
Added:
#name : 'MFTSetGroupTask',
3
Added:
#superclass : 'WATask',
4
Added:
#instVars : [
5
Added:
'exercises',
6
Added:
'exerciseCount',
7
Added:
'setGroup'
8
Added:
],
9
Added:
#category : 'MyFitnessTracker-Components-Tasks',
10
Added:
#package : 'MyFitnessTracker-Components',
11
Added:
#tag : 'Tasks'
12
Added:
}
13
Added:
14
Added:
{ #category : 'as yet unclassified' }
15
Added:
MFTSetGroupTask class >> forExerciseCount: anInteger [
16
Added:
17
Added:
^ self new
18
Added:
exerciseCount: anInteger;
19
Added:
yourself
20
Added:
]
21
Added:
22
Added:
{ #category : 'accessing' }
23
Added:
MFTSetGroupTask >> exerciseCount: anObject [
24
Added:
25
Added:
exerciseCount := anObject
26
Added:
]
27
Added:
28
Added:
{ #category : 'accessing' }
29
Added:
MFTSetGroupTask >> exercises [
30
Added:
31
Added:
^ exercises
32
Added:
]
33
Added:
34
Added:
{ #category : 'accessing' }
35
Added:
MFTSetGroupTask >> exercises: anObject [
36
Added:
37
Added:
exercises := anObject
38
Added:
]
39
Added:
40
Added:
{ #category : 'running' }
41
Added:
MFTSetGroupTask >> go [
42
Added:
43
Added:
exercises := self call:
44
Added:
(MFTExercisesForm forExerciseCount: exerciseCount).
45
Added:
exercises ifNil: [ self answer: nil ].
46
Added:
47
Added:
setGroup := self call: (MFTSetGroupForm forExercises: exercises).
48
Added:
setGroup ifNil: [ self answer: nil ].
49
Added:
50
Added:
self answer: setGroup
51
Added:
]
52
Added:
53
Added:
{ #category : 'accessing' }
54
Added:
MFTSetGroupTask >> setGroups [
55
Added:
56
Added:
^ setGroups
57
Added:
]
58
Added:
59
Added:
{ #category : 'accessing' }
60
Added:
MFTSetGroupTask >> setGroups: anObject [
61
Added:
62
Added:
setGroups := anObject
63
Added:
]
src/MyFitnessTracker-Components/MFTSetGroupsDialog.class.st
@@ -0,0 +1,179 @@
1
Added:
Class {
2
Added:
#name : 'MFTSetGroupsDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'setGroups'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'as yet unclassified' }
13
Added:
MFTSetGroupsDialog >> addExercise [
14
Added:
15
Added:
| setGroup |
16
Added:
setGroup := self call: (MFTSetGroupDialog new heading: 'Exercise').
17
Added:
setGroup ifNotNil: [ setGroups add: setGroup ]
18
Added:
]
19
Added:
20
Added:
{ #category : 'as yet unclassified' }
21
Added:
MFTSetGroupsDialog >> editSetGroup: aMFTSetGroup [
22
Added:
23
Added:
| oldSetGroup newSetGroup |
24
Added:
oldSetGroup := aMFTSetGroup.
25
Added:
newSetGroup := self call: (MFTSetGroupDialog edit: aMFTSetGroup copy).
26
Added:
newSetGroup ifNotNil: [
27
Added:
setGroups at: (setGroups indexOf: oldSetGroup) put: newSetGroup ]
28
Added:
]
29
Added:
30
Added:
{ #category : 'as yet unclassified' }
31
Added:
MFTSetGroupsDialog >> initialize [
32
Added:
33
Added:
super initialize.
34
Added:
setGroups := OrderedCollection new
35
Added:
]
36
Added:
37
Added:
{ #category : 'as yet unclassified' }
38
Added:
MFTSetGroupsDialog >> renderAddSetGroupButtonsOn: html [
39
Added:
40
Added:
html outlineButton
41
Added:
bePrimary;
42
Added:
beSmall;
43
Added:
callback: [ self addExercise ];
44
Added:
with: 'Add exercise'
45
Added:
]
46
Added:
47
Added:
{ #category : 'as yet unclassified' }
48
Added:
MFTSetGroupsDialog >> renderContentOn: html [
49
Added:
50
Added:
self renderDialogOn: html with: [
51
Added:
setGroups ifNotEmpty: [ self renderSetsTableOn: html ].
52
Added:
self renderAddSetGroupButtonsOn: html ]
53
Added:
]
54
Added:
55
Added:
{ #category : 'as yet unclassified' }
56
Added:
MFTSetGroupsDialog >> renderLoad [
57
Added:
58
Added:
^ (setGroups flatCollect: #sets) anySatisfy: #hasLoad
59
Added:
]
60
Added:
61
Added:
{ #category : 'as yet unclassified' }
62
Added:
MFTSetGroupsDialog >> renderRestAfter [
63
Added:
64
Added:
^ (setGroups flatCollect: #sets) anySatisfy: #hasRestAfter
65
Added:
]
66
Added:
67
Added:
{ #category : 'as yet unclassified' }
68
Added:
MFTSetGroupsDialog >> renderRpe [
69
Added:
70
Added:
^ (setGroups flatCollect: #sets) anySatisfy: #hasRpe
71
Added:
]
72
Added:
73
Added:
{ #category : 'as yet unclassified' }
74
Added:
MFTSetGroupsDialog >> renderSetGroupButtonsOn: html For: setGroup [
75
Added:
76
Added:
html buttonGroup: [
77
Added:
html outlineButton
78
Added:
beDanger;
79
Added:
beSmall;
80
Added:
callback: [ setGroups remove: setGroup ];
81
Added:
with: 'Remove'.
82
Added:
html outlineButton
83
Added:
beSecondary;
84
Added:
beSmall;
85
Added:
callback: [ self editSetGroup: setGroup ];
86
Added:
with: 'Edit'.
87
Added:
html outlineButton
88
Added:
beSecondary;
89
Added:
beSmall;
90
Added:
callback: [
91
Added:
setGroups add: setGroup afterIndex: (setGroups indexOf: setGroup) ];
92
Added:
with: 'Duplicate' ]
93
Added:
]
94
Added:
95
Added:
{ #category : 'as yet unclassified' }
96
Added:
MFTSetGroupsDialog >> renderSetGroupsRowsOn: html [
97
Added:
98
Added:
| renderLoad renderRpe renderRestAfter |
99
Added:
renderLoad := self renderLoad.
100
Added:
renderRpe := self renderRpe.
101
Added:
renderRestAfter := self renderRestAfter.
102
Added:
setGroups do: [ :setGroup |
103
Added:
html tableRow with: [
104
Added:
html tableData
105
Added:
attributeAt: 'rowspan' put: setGroup sets size + 1;
106
Added:
style: 'vertical-align: middle';
107
Added:
with: [ self renderSetGroupButtonsOn: html For: setGroup ] ].
108
Added:
setGroup sets do: [ :set |
109
Added:
html tableRow: [
110
Added:
html tableData: set exercise englishName.
111
Added:
html tableData: set reps.
112
Added:
setGroup hasLoad ifTrue: [ html tableData: set load ].
113
Added:
setGroup hasRpe ifTrue: [ html tableData: set rpe ].
114
Added:
setGroup hasRestAfter ifTrue: [ html tableData: set restAfter ] ] ] ]
115
Added:
]
116
Added:
117
Added:
{ #category : 'as yet unclassified' }
118
Added:
MFTSetGroupsDialog >> renderSetGroupsTableHeaderOn: html [
119
Added:
120
Added:
html tableHead: [
121
Added:
html
122
Added:
tableHeading;
123
Added:
tableHeading: 'Exercise';
124
Added:
tableHeading: 'Reps'.
125
Added:
self renderLoad ifTrue: [ html tableHeading: 'Load' ].
126
Added:
self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
127
Added:
self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
128
Added:
]
129
Added:
130
Added:
{ #category : 'as yet unclassified' }
131
Added:
MFTSetGroupsDialog >> renderSetsTableHeaderOn: html [
132
Added:
133
Added:
html tableHead: [
134
Added:
html
135
Added:
tableHeading;
136
Added:
tableHeading: 'Exercise';
137
Added:
tableHeading: 'Reps'.
138
Added:
self renderLoad ifTrue: [ html tableHeading: 'Load' ].
139
Added:
self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
140
Added:
self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
141
Added:
]
142
Added:
143
Added:
{ #category : 'as yet unclassified' }
144
Added:
MFTSetGroupsDialog >> renderSetsTableOn: html [
145
Added:
146
Added:
html table
147
Added:
class: 'table table-bordered';
148
Added:
with: [
149
Added:
self renderSetGroupsTableHeaderOn: html.
150
Added:
self renderSetGroupsRowsOn: html ]
151
Added:
]
152
Added:
153
Added:
{ #category : 'as yet unclassified' }
154
Added:
MFTSetGroupsDialog >> renderSubmitOn: html [
155
Added:
156
Added:
html buttonGroup: [
157
Added:
html formButton
158
Added:
beSubmit;
159
Added:
bePrimary;
160
Added:
callback: [ self answer: setGroups ];
161
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
162
Added:
html formButton
163
Added:
beSubmit;
164
Added:
beSecondary;
165
Added:
callback: [ self answer: OrderedCollection new ];
166
Added:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
167
Added:
]
168
Added:
169
Added:
{ #category : 'as yet unclassified' }
170
Added:
MFTSetGroupsDialog >> setGroups [
171
Added:
172
Added:
^ setGroups
173
Added:
]
174
Added:
175
Added:
{ #category : 'as yet unclassified' }
176
Added:
MFTSetGroupsDialog >> setGroups: anObject [
177
Added:
178
Added:
setGroups := anObject
179
Added:
]
src/MyFitnessTracker-Components/MFTSetGroupsDialogOld.class.st
@@ -0,0 +1,179 @@
1
Added:
Class {
2
Added:
#name : 'MFTSetGroupsDialogOld',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'setGroups'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'adding' }
13
Added:
MFTSetGroupsDialogOld >> addExercise [
14
Added:
15
Added:
| setGroup |
16
Added:
setGroup := self call: (MFTSetGroupDialog new heading: 'Exercise').
17
Added:
setGroup ifNotNil: [ setGroups add: setGroup ]
18
Added:
]
19
Added:
20
Added:
{ #category : 'rendering' }
21
Added:
MFTSetGroupsDialogOld >> editSetGroup: aMFTSetGroup [
22
Added:
23
Added:
| oldSetGroup newSetGroup |
24
Added:
oldSetGroup := aMFTSetGroup.
25
Added:
newSetGroup := self call: (MFTSetGroupDialog edit: aMFTSetGroup copy).
26
Added:
newSetGroup ifNotNil: [
27
Added:
setGroups at: (setGroups indexOf: oldSetGroup) put: newSetGroup ]
28
Added:
]
29
Added:
30
Added:
{ #category : 'initialization' }
31
Added:
MFTSetGroupsDialogOld >> initialize [
32
Added:
33
Added:
super initialize.
34
Added:
setGroups := OrderedCollection new
35
Added:
]
36
Added:
37
Added:
{ #category : 'rendering' }
38
Added:
MFTSetGroupsDialogOld >> renderAddSetGroupButtonsOn: html [
39
Added:
40
Added:
html outlineButton
41
Added:
bePrimary;
42
Added:
beSmall;
43
Added:
callback: [ self addExercise ];
44
Added:
with: 'Add exercise'
45
Added:
]
46
Added:
47
Added:
{ #category : 'rendering' }
48
Added:
MFTSetGroupsDialogOld >> renderContentOn: html [
49
Added:
50
Added:
self renderDialogOn: html with: [
51
Added:
setGroups ifNotEmpty: [ self renderSetsTableOn: html ].
52
Added:
self renderAddSetGroupButtonsOn: html ]
53
Added:
]
54
Added:
55
Added:
{ #category : 'as yet unclassified' }
56
Added:
MFTSetGroupsDialogOld >> renderLoad [
57
Added:
58
Added:
^ (setGroups flatCollect: #sets) anySatisfy: #hasLoad
59
Added:
]
60
Added:
61
Added:
{ #category : 'as yet unclassified' }
62
Added:
MFTSetGroupsDialogOld >> renderRestAfter [
63
Added:
64
Added:
^ (setGroups flatCollect: #sets) anySatisfy: #hasRestAfter
65
Added:
]
66
Added:
67
Added:
{ #category : 'as yet unclassified' }
68
Added:
MFTSetGroupsDialogOld >> renderRpe [
69
Added:
70
Added:
^ (setGroups flatCollect: #sets) anySatisfy: #hasRpe
71
Added:
]
72
Added:
73
Added:
{ #category : 'as yet unclassified' }
74
Added:
MFTSetGroupsDialogOld >> renderSetGroupButtonsOn: html For: setGroup [
75
Added:
76
Added:
html buttonGroup: [
77
Added:
html outlineButton
78
Added:
beDanger;
79
Added:
beSmall;
80
Added:
callback: [ setGroups remove: setGroup ];
81
Added:
with: 'Remove'.
82
Added:
html outlineButton
83
Added:
beSecondary;
84
Added:
beSmall;
85
Added:
callback: [ self editSetGroup: setGroup ];
86
Added:
with: 'Edit'.
87
Added:
html outlineButton
88
Added:
beSecondary;
89
Added:
beSmall;
90
Added:
callback: [
91
Added:
setGroups add: setGroup afterIndex: (setGroups indexOf: setGroup) ];
92
Added:
with: 'Duplicate' ]
93
Added:
]
94
Added:
95
Added:
{ #category : 'as yet unclassified' }
96
Added:
MFTSetGroupsDialogOld >> renderSetGroupsRowsOn: html [
97
Added:
98
Added:
| renderLoad renderRpe renderRestAfter |
99
Added:
renderLoad := self renderLoad.
100
Added:
renderRpe := self renderRpe.
101
Added:
renderRestAfter := self renderRestAfter.
102
Added:
setGroups do: [ :setGroup |
103
Added:
html tableRow with: [
104
Added:
html tableData
105
Added:
attributeAt: 'rowspan' put: setGroup sets size + 1;
106
Added:
style: 'vertical-align: middle';
107
Added:
with: [ self renderSetGroupButtonsOn: html For: setGroup ] ].
108
Added:
setGroup sets do: [ :set |
109
Added:
html tableRow: [
110
Added:
html tableData: set exercise englishName.
111
Added:
html tableData: set reps.
112
Added:
setGroup hasLoad ifTrue: [ html tableData: set load ].
113
Added:
setGroup hasRpe ifTrue: [ html tableData: set rpe ].
114
Added:
setGroup hasRestAfter ifTrue: [ html tableData: set restAfter ] ] ] ]
115
Added:
]
116
Added:
117
Added:
{ #category : 'as yet unclassified' }
118
Added:
MFTSetGroupsDialogOld >> renderSetGroupsTableHeaderOn: html [
119
Added:
120
Added:
html tableHead: [
121
Added:
html
122
Added:
tableHeading;
123
Added:
tableHeading: 'Exercise';
124
Added:
tableHeading: 'Reps'.
125
Added:
self renderLoad ifTrue: [ html tableHeading: 'Load' ].
126
Added:
self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
127
Added:
self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
128
Added:
]
129
Added:
130
Added:
{ #category : 'as yet unclassified' }
131
Added:
MFTSetGroupsDialogOld >> renderSetsTableHeaderOn: html [
132
Added:
133
Added:
html tableHead: [
134
Added:
html
135
Added:
tableHeading;
136
Added:
tableHeading: 'Exercise';
137
Added:
tableHeading: 'Reps'.
138
Added:
self renderLoad ifTrue: [ html tableHeading: 'Load' ].
139
Added:
self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
140
Added:
self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
141
Added:
]
142
Added:
143
Added:
{ #category : 'as yet unclassified' }
144
Added:
MFTSetGroupsDialogOld >> renderSetsTableOn: html [
145
Added:
146
Added:
html table
147
Added:
class: 'table table-bordered';
148
Added:
with: [
149
Added:
self renderSetGroupsTableHeaderOn: html.
150
Added:
self renderSetGroupsRowsOn: html ]
151
Added:
]
152
Added:
153
Added:
{ #category : 'rendering' }
154
Added:
MFTSetGroupsDialogOld >> renderSubmitOn: html [
155
Added:
156
Added:
html buttonGroup: [
157
Added:
html formButton
158
Added:
beSubmit;
159
Added:
bePrimary;
160
Added:
callback: [ self answer: setGroups ];
161
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
162
Added:
html formButton
163
Added:
beSubmit;
164
Added:
beSecondary;
165
Added:
callback: [ self answer: OrderedCollection new ];
166
Added:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
167
Added:
]
168
Added:
169
Added:
{ #category : 'accessing' }
170
Added:
MFTSetGroupsDialogOld >> setGroups [
171
Added:
172
Added:
^ setGroups
173
Added:
]
174
Added:
175
Added:
{ #category : 'accessing' }
176
Added:
MFTSetGroupsDialogOld >> setGroups: anObject [
177
Added:
178
Added:
setGroups := anObject
179
Added:
]
src/MyFitnessTracker-Components/MFTSetGroupsFormComponent.class.st
@@ -0,0 +1,62 @@
1
Added:
Class {
2
Added:
#name : 'MFTSetGroupsFormComponent',
3
Added:
#superclass : 'MFTFormComponent',
4
Added:
#category : 'MyFitnessTracker-Components-Forms',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Forms'
7
Added:
}
8
Added:
9
Added:
{ #category : 'adding' }
10
Added:
MFTSetGroupsFormComponent >> addSetGroupsForExerciseCount: anInteger [
11
Added:
12
Added:
| setGroup |
13
Added:
setGroup := self session pageManager activePage call:
14
Added:
(MFTSetGroupTask forExerciseCount: anInteger).
15
Added:
setGroup ifNotNil: [ modelObject addSetGroup: setGroup ]
16
Added:
]
17
Added:
18
Added:
{ #category : 'initialization' }
19
Added:
MFTSetGroupsFormComponent >> initialize [
20
Added:
21
Added:
super initialize.
22
Added:
heading := 'Exercises'
23
Added:
]
24
Added:
25
Added:
{ #category : 'rendering' }
26
Added:
MFTSetGroupsFormComponent >> renderContentOn: html [
27
Added:
28
Added:
self renderFormComponentOn: html with: [
29
Added:
modelObject renderSetGroupsTableOn: html.
30
Added:
self renderSetGroupButtonsOn: html ]
31
Added:
]
32
Added:
33
Added:
{ #category : 'rendering' }
34
Added:
MFTSetGroupsFormComponent >> renderSetGroupButtonsOn: html [
35
Added:
36
Added:
html buttonGroup: [
37
Added:
html outlineButton
38
Added:
bePrimary;
39
Added:
beSmall;
40
Added:
callback: [ self addSetGroupsForExerciseCount: 1 ];
41
Added:
with: 'Add exercise'.
42
Added:
html outlineButton
43
Added:
beSecondary;
44
Added:
beSmall;
45
Added:
callback: [ self addSetGroupsForExerciseCount: 2 ];
46
Added:
with: 'Add superset'.
47
Added:
html outlineButton
48
Added:
beSecondary;
49
Added:
beSmall;
50
Added:
callback: [ self addSetGroupsForExerciseCount: 3 ];
51
Added:
with: 'Add tri-set'.
52
Added:
html outlineButton
53
Added:
beSecondary;
54
Added:
beSmall;
55
Added:
callback: [ self addSetGroupsForExerciseCount: 4 ];
56
Added:
with: 'Add giant set'.
57
Added:
html outlineButton
58
Added:
beDanger;
59
Added:
beSmall;
60
Added:
callback: [ modelObject setGroups removeAll ];
61
Added:
with: 'Remove all' ]
62
Added:
]
src/MyFitnessTracker-Components/MFTSetsDialog.class.st
@@ -0,0 +1,10 @@
1
Added:
Class {
2
Added:
#name : 'MFTSetsDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'sets'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
src/MyFitnessTracker-Components/MFTStringDialog.class.st
@@ -0,0 +1,58 @@
1
Added:
Class {
2
Added:
#name : 'MFTStringDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'string'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'as yet unclassified' }
13
Added:
MFTStringDialog class >> edit: aString [
14
Added:
15
Added:
^ self new
16
Added:
action: #edit;
17
Added:
string: aString;
18
Added:
yourself
19
Added:
]
20
Added:
21
Added:
{ #category : 'as yet unclassified' }
22
Added:
MFTStringDialog >> initialize [
23
Added:
24
Added:
super initialize.
25
Added:
string := String new
26
Added:
]
27
Added:
28
Added:
{ #category : 'as yet unclassified' }
29
Added:
MFTStringDialog >> renderContentOn: html [
30
Added:
31
Added:
self renderDialogOn: html with: [
32
Added:
html textInput formControl
33
Added:
value: string;
34
Added:
callback: [ :value | string := value ] ]
35
Added:
]
36
Added:
37
Added:
{ #category : 'as yet unclassified' }
38
Added:
MFTStringDialog >> renderSubmitOn: html [
39
Added:
40
Added:
html buttonGroup: [
41
Added:
html formButton
42
Added:
beSubmit;
43
Added:
bePrimary;
44
Added:
callback: [ self answer: string ];
45
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]) ]
46
Added:
]
47
Added:
48
Added:
{ #category : 'as yet unclassified' }
49
Added:
MFTStringDialog >> string [
50
Added:
51
Added:
^ string
52
Added:
]
53
Added:
54
Added:
{ #category : 'as yet unclassified' }
55
Added:
MFTStringDialog >> string: anObject [
56
Added:
57
Added:
string := anObject
58
Added:
]
src/MyFitnessTracker-Components/MFTUserForm.class.st
@@ -0,0 +1,45 @@
1
Added:
Class {
2
Added:
#name : 'MFTUserForm',
3
Added:
#superclass : 'MFTForm',
4
Added:
#category : 'MyFitnessTracker-Components-Forms',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Forms'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTUserForm class >> edit: aMFTWorkout [
11
Added:
12
Added:
^ self new
13
Added:
modelObject: aMFTWorkout;
14
Added:
yourself
15
Added:
]
16
Added:
17
Added:
{ #category : 'initialization' }
18
Added:
MFTUserForm >> initialize [
19
Added:
20
Added:
super initialize.
21
Added:
modelObject := MFTWorkout new.
22
Added:
heading := 'Workout'
23
Added:
]
24
Added:
25
Added:
{ #category : 'rendering' }
26
Added:
MFTUserForm >> renderContentOn: html [
27
Added:
28
Added:
self renderFormOn: html with: [
29
Added:
html
30
Added:
render: (MFTFormComponent set: #date);
31
Added:
render: (MFTFormComponent set: #setGroups);
32
Added:
render: (MFTFormComponent set: #primaryMuscleGroup);
33
Added:
render: (MFTFormComponent set: #secondaryMuscleGroups) ]
34
Added:
]
35
Added:
36
Added:
{ #category : 'as yet unclassified' }
37
Added:
MFTUserForm >> submitForm [
38
Added:
39
Added:
modelObject setGroups ifEmpty: [
40
Added:
(self call:
41
Added:
(MFTConfirmForm new modelObject: 'No exercises were specified.'))
42
Added:
ifFalse: [ self answer: nil ] ].
43
Added:
44
Added:
self answer: modelObject
45
Added:
]
src/MyFitnessTracker-Components/MFTUserLandingPage.class.st
@@ -0,0 +1,15 @@
1
Added:
Class {
2
Added:
#name : 'MFTUserLandingPage',
3
Added:
#superclass : 'MFTLandingPage',
4
Added:
#category : 'MyFitnessTracker-Components-Pages',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Pages'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTUserLandingPage >> defaultCards [
11
Added:
12
Added:
^ {
13
Added:
MFTMainUserCard new.
14
Added:
MFTWorkoutsCard new }
15
Added:
]
src/MyFitnessTracker-Components/MFTUserTask.class.st
@@ -0,0 +1,85 @@
1
Added:
Class {
2
Added:
#name : 'MFTUserTask',
3
Added:
#superclass : 'WATask',
4
Added:
#instVars : [
5
Added:
'user',
6
Added:
'action'
7
Added:
],
8
Added:
#category : 'MyFitnessTracker-Components-Tasks',
9
Added:
#package : 'MyFitnessTracker-Components',
10
Added:
#tag : 'Tasks'
11
Added:
}
12
Added:
13
Added:
{ #category : 'actions' }
14
Added:
MFTUserTask class >> edit: aMFTUser [
15
Added:
16
Added:
^ self new
17
Added:
user: aMFTUser;
18
Added:
action: #edit;
19
Added:
yourself
20
Added:
]
21
Added:
22
Added:
{ #category : 'accessing' }
23
Added:
MFTUserTask >> action [
24
Added:
25
Added:
^ action
26
Added:
]
27
Added:
28
Added:
{ #category : 'accessing' }
29
Added:
MFTUserTask >> action: anObject [
30
Added:
31
Added:
action := anObject
32
Added:
]
33
Added:
34
Added:
{ #category : 'running' }
35
Added:
MFTUserTask >> go [
36
Added:
37
Added:
user username: (self call: (MFTStringDialog new
38
Added:
action: action;
39
Added:
heading: 'username';
40
Added:
string: user username;
41
Added:
completion: 20)).
42
Added:
user passwordHashFromString: (self call: (MFTStringDialog new
43
Added:
action: action;
44
Added:
heading: 'password';
45
Added:
completion: 40)).
46
Added:
user dateOfBirth: (self call: (MFTDateDialog new
47
Added:
action: action;
48
Added:
heading: 'date of birth';
49
Added:
date: user dateOfBirth;
50
Added:
completion: 60)).
51
Added:
user realFirstName: (self call: (MFTStringDialog new
52
Added:
action: action;
53
Added:
heading: 'real first name';
54
Added:
string: user realFirstName;
55
Added:
completion: 80)).
56
Added:
user realLastName: (self call: (MFTStringDialog new
57
Added:
heading: 'real last name';
58
Added:
string: user realLastName;
59
Added:
completion: 100)).
60
Added:
61
Added:
(self call: (MFTConfirmDialog new
62
Added:
action: action;
63
Added:
component: user))
64
Added:
ifTrue: [ self answer: user ]
65
Added:
ifFalse: [ self answer: nil ]
66
Added:
]
67
Added:
68
Added:
{ #category : 'initialization' }
69
Added:
MFTUserTask >> initialize [
70
Added:
71
Added:
super initialize.
72
Added:
user := MFTUser new
73
Added:
]
74
Added:
75
Added:
{ #category : 'accessing' }
76
Added:
MFTUserTask >> user [
77
Added:
78
Added:
^ user
79
Added:
]
80
Added:
81
Added:
{ #category : 'accessing' }
82
Added:
MFTUserTask >> user: anObject [
83
Added:
84
Added:
user := anObject
85
Added:
]
src/MyFitnessTracker-Components/MFTWeightDialog.class.st
@@ -0,0 +1,76 @@
1
Added:
Class {
2
Added:
#name : 'MFTWeightDialog',
3
Added:
#superclass : 'MFTDialog',
4
Added:
#instVars : [
5
Added:
'weight'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Dialogs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Dialogs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'actions' }
13
Added:
MFTWeightDialog class >> edit: aMFTWeight [
14
Added:
15
Added:
^ self new
16
Added:
action: #edit;
17
Added:
weight: aMFTWeight;
18
Added:
yourself
19
Added:
]
20
Added:
21
Added:
{ #category : 'initialization' }
22
Added:
MFTWeightDialog >> initialize [
23
Added:
24
Added:
super initialize.
25
Added:
weight := MFTWeight new
26
Added:
]
27
Added:
28
Added:
{ #category : 'rendering' }
29
Added:
MFTWeightDialog >> renderContentOn: html [
30
Added:
31
Added:
self renderDialogOn: html with: [
32
Added:
html formGroup: [
33
Added:
html label: 'Date'.
34
Added:
html dateInput5 formControl
35
Added:
value: weight date yyyymmdd;
36
Added:
callback: [ :value |
37
Added:
weight date: (Date readFrom: value pattern: 'yyyy-mm-dd') ] ].
38
Added:
html formGroup: [
39
Added:
html label: 'Weight'.
40
Added:
html numberInput formControl
41
Added:
value: (weight value = 0
42
Added:
ifTrue: [ nil ]
43
Added:
ifFalse: [ weight value ]);
44
Added:
placeholder: weight value;
45
Added:
callback: [ :value |
46
Added:
value ifNotEmpty: [ weight value: value asNumber ] ] ] ]
47
Added:
]
48
Added:
49
Added:
{ #category : 'rendering' }
50
Added:
MFTWeightDialog >> renderSubmitOn: html [
51
Added:
52
Added:
html buttonGroup: [
53
Added:
html formButton
54
Added:
beSubmit;
55
Added:
bePrimary;
56
Added:
callback: [
57
Added:
self answer: (weight value ifNotNil: [ weight ] ifNil: [ nil ]) ];
58
Added:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
59
Added:
html formButton
60
Added:
beSubmit;
61
Added:
beSecondary;
62
Added:
callback: [ self answer: nil ];
63
Added:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
64
Added:
]
65
Added:
66
Added:
{ #category : 'accessing' }
67
Added:
MFTWeightDialog >> weight [
68
Added:
69
Added:
^ weight
70
Added:
]
71
Added:
72
Added:
{ #category : 'accessing' }
73
Added:
MFTWeightDialog >> weight: aMFTWeight [
74
Added:
75
Added:
weight := aMFTWeight
76
Added:
]
src/MyFitnessTracker-Components/MFTWeightVsTimeGraph.class.st
@@ -0,0 +1,29 @@
1
Added:
Class {
2
Added:
#name : 'MFTWeightVsTimeGraph',
3
Added:
#superclass : 'MFTGraph',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'rendering' }
10
Added:
MFTWeightVsTimeGraph >> renderContentOn: html [
11
Added:
12
Added:
labels := self session user weights collect: [ :weight |
13
Added:
weight date yyyymmdd ].
14
Added:
data := self session user weights collect: #value.
15
Added:
16
Added:
html canvas id: 'weightVsTimeChart'.
17
Added:
html script:
18
Added:
'var ctx = document.getElementById("weightVsTimeChart").getContext("2d");
19
Added:
var weightChart = new Chart(ctx, {
20
Added:
type: "line",
21
Added:
data: {
22
Added:
labels: ' , labels asJavascript , ',
23
Added:
datasets: [{
24
Added:
label: "weight",
25
Added:
data: ' , data asJavascript , ',
26
Added:
}]
27
Added:
},
28
Added:
});'
29
Added:
]
src/MyFitnessTracker-Components/MFTWeightsCard.class.st
@@ -0,0 +1,54 @@
1
Added:
Class {
2
Added:
#name : 'MFTWeightsCard',
3
Added:
#superclass : 'MFTCard',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'adding' }
10
Added:
MFTWeightsCard >> addWeight [
11
Added:
12
Added:
| newWeight |
13
Added:
newWeight := self session pageManager activePage call:
14
Added:
MFTWeightDialog new.
15
Added:
(self session user weights anySatisfy: [ :existingWeight |
16
Added:
newWeight date = existingWeight date and: [
17
Added:
newWeight value = existingWeight value ] ]) ifTrue: [ ^ self ].
18
Added:
19
Added:
self session user isLoggedIn
20
Added:
ifTrue: [ self session db insert: newWeight ]
21
Added:
ifFalse: [ self session user weights add: newWeight ]
22
Added:
]
23
Added:
24
Added:
{ #category : 'rendering' }
25
Added:
MFTWeightsCard >> renderContentOn: html [
26
Added:
27
Added:
self renderCardOn: html with: [
28
Added:
html cardHeader: 'Weights'.
29
Added:
html cardBody: [
30
Added:
html paragraph: 'Great job logging your weight '
31
Added:
, self session user weights size asString , ' times!'.
32
Added:
html form
33
Added:
class: 'mb-3';
34
Added:
with: [
35
Added:
html buttonGroup
36
Added:
beSmall;
37
Added:
with: [
38
Added:
html outlineButton
39
Added:
bePrimary;
40
Added:
callback: [ self addWeight ];
41
Added:
with: 'Log weight'.
42
Added:
html outlineButton
43
Added:
beSecondary;
44
Added:
callback: [ self viewAnalytics ];
45
Added:
with: 'View progress' ] ].
46
Added:
html render: MFTWeightVsTimeGraph new ] ]
47
Added:
]
48
Added:
49
Added:
{ #category : 'as yet unclassified' }
50
Added:
MFTWeightsCard >> viewAnalytics [
51
Added:
52
Added:
self session pageManager activePage call:
53
Added:
(MFTAnalyticsPage for: #weights)
54
Added:
]
src/MyFitnessTracker-Components/MFTWeightsLog.class.st
@@ -0,0 +1,141 @@
1
Added:
Class {
2
Added:
#name : 'MFTWeightsLog',
3
Added:
#superclass : 'MFTLog',
4
Added:
#instVars : [
5
Added:
'weights'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Logs',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Logs'
10
Added:
}
11
Added:
12
Added:
{ #category : 'adding' }
13
Added:
MFTWeightsLog >> addWeight [
14
Added:
15
Added:
| newWeight |
16
Added:
newWeight := self call: MFTWeightDialog new.
17
Added:
newWeight ifNil: [ ^ self ].
18
Added:
(weights anySatisfy: [ :existingWeight |
19
Added:
existingWeight date = newWeight date and: [
20
Added:
existingWeight value = newWeight value ] ]) ifTrue: [ ^ self ].
21
Added:
22
Added:
self session user isLoggedIn
23
Added:
ifTrue: [ self session db insert: newWeight ]
24
Added:
ifFalse: [ weights add: newWeight ]
25
Added:
]
26
Added:
27
Added:
{ #category : 'accessing' }
28
Added:
MFTWeightsLog >> editWeight: oldWeight [
29
Added:
30
Added:
| newWeight |
31
Added:
newWeight := self call: (MFTWeightDialog edit: oldWeight copy).
32
Added:
(oldWeight value = newWeight value and: [
33
Added:
oldWeight date = newWeight date ]) ifTrue: [ ^ self ].
34
Added:
35
Added:
self session user isLoggedIn
36
Added:
ifTrue: [ self session db update: oldWeight with: newWeight ]
37
Added:
ifFalse: [
38
Added:
weights
39
Added:
remove: oldWeight;
40
Added:
add: newWeight ]
41
Added:
]
42
Added:
43
Added:
{ #category : 'initialization' }
44
Added:
MFTWeightsLog >> initialize [
45
Added:
46
Added:
super initialize.
47
Added:
title := 'weights'.
48
Added:
weights := OrderedCollection new
49
Added:
]
50
Added:
51
Added:
{ #category : 'as yet unclassified' }
52
Added:
MFTWeightsLog >> randomWittyCTA [
53
Added:
54
Added:
| listOfStrings randomString |
55
Added:
listOfStrings := #( 'First time?' 'Get your sweat on!'
56
Added:
'Go go go!' 'Go get ''em, Tiger!' ).
57
Added:
randomString := listOfStrings atRandom.
58
Added:
^ randomString
59
Added:
]
60
Added:
61
Added:
{ #category : 'accessing' }
62
Added:
MFTWeightsLog >> removeWeight: aMFTWeight [
63
Added:
64
Added:
self session user isLoggedIn
65
Added:
ifTrue: [ self session db delete: aMFTWeight ]
66
Added:
ifFalse: [ weights remove: aMFTWeight ]
67
Added:
]
68
Added:
69
Added:
{ #category : 'rendering' }
70
Added:
MFTWeightsLog >> renderActionsButtonGroupOn: html [
71
Added:
72
Added:
html form: [
73
Added:
html buttonGroup: [
74
Added:
html formButton
75
Added:
bePrimary;
76
Added:
callback: [ self addWeight ];
77
Added:
with: 'Log weight'.
78
Added:
html formButton
79
Added:
beSecondary;
80
Added:
callback: [ self viewAnalytics ];
81
Added:
with: 'View progress' ] ]
82
Added:
]
83
Added:
84
Added:
{ #category : 'rendering' }
85
Added:
MFTWeightsLog >> renderContentOn: html [
86
Added:
87
Added:
weights := self session user weights sort: [ :a :b | a date > b date ].
88
Added:
self renderLogOn: html with: [
89
Added:
weights ifEmpty: [
90
Added:
html jumbotron: [ html heading: self randomWittyCTA ] ].
91
Added:
self renderActionsButtonGroupOn: html.
92
Added:
weights ifNotEmpty: [ self renderWeightsAsTableOn: html ] ]
93
Added:
]
94
Added:
95
Added:
{ #category : 'rendering' }
96
Added:
MFTWeightsLog >> renderWeightActionsButtonGroupOn: html for: aMFTWeight [
97
Added:
98
Added:
html form: [
99
Added:
html buttonGroup
100
Added:
beSmall;
101
Added:
with: [
102
Added:
html formButton
103
Added:
beSecondary;
104
Added:
formControl;
105
Added:
callback: [ self editWeight: aMFTWeight ];
106
Added:
with: 'Edit'.
107
Added:
html formButton
108
Added:
beDanger;
109
Added:
formControl;
110
Added:
callback: [ self removeWeight: aMFTWeight ];
111
Added:
with: 'Delete' ] ]
112
Added:
]
113
Added:
114
Added:
{ #category : 'rendering' }
115
Added:
MFTWeightsLog >> renderWeightsAsTableOn: html [
116
Added:
117
Added:
html table
118
Added:
class: 'table table-striped';
119
Added:
with: [
120
Added:
html tableHead: [
121
Added:
html
122
Added:
tableHeading;
123
Added:
tableHeading: 'Date'.
124
Added:
html tableHeading
125
Added:
style: 'text-align: right';
126
Added:
with: 'Value' ].
127
Added:
weights do: [ :weight |
128
Added:
html tableRow: [
129
Added:
html tableData: [
130
Added:
self renderWeightActionsButtonGroupOn: html for: weight ].
131
Added:
html tableData: weight date yyyymmdd.
132
Added:
html tableData
133
Added:
style: 'text-align: right';
134
Added:
with: weight value ] ] ]
135
Added:
]
136
Added:
137
Added:
{ #category : 'adding' }
138
Added:
MFTWeightsLog >> viewAnalytics [
139
Added:
140
Added:
self call: (MFTAnalyticsPage for: #weights)
141
Added:
]
src/MyFitnessTracker-Components/MFTWidget.class.st
@@ -0,0 +1,46 @@
1
Added:
Class {
2
Added:
#name : 'MFTWidget',
3
Added:
#superclass : 'SBSComponent',
4
Added:
#instVars : [
5
Added:
'title'
6
Added:
],
7
Added:
#category : 'MyFitnessTracker-Components-Widgets',
8
Added:
#package : 'MyFitnessTracker-Components',
9
Added:
#tag : 'Widgets'
10
Added:
}
11
Added:
12
Added:
{ #category : 'accessing' }
13
Added:
MFTWidget >> defaultDescription [
14
Added:
15
Added:
^ 'I am a ' , self class name
16
Added:
]
17
Added:
18
Added:
{ #category : 'initialization' }
19
Added:
MFTWidget >> initialize [
20
Added:
21
Added:
super initialize.
22
Added:
title := String new
23
Added:
]
24
Added:
25
Added:
{ #category : 'rendering' }
26
Added:
MFTWidget >> renderContentOn: html [
27
Added:
28
Added:
html div: [
29
Added:
title ifNotEmpty: [
30
Added:
html heading
31
Added:
level5;
32
Added:
with: self title ].
33
Added:
html paragraph: self defaultDescription ]
34
Added:
]
35
Added:
36
Added:
{ #category : 'accessing' }
37
Added:
MFTWidget >> title [
38
Added:
39
Added:
^ title
40
Added:
]
41
Added:
42
Added:
{ #category : 'accessing' }
43
Added:
MFTWidget >> title: aString [
44
Added:
45
Added:
title := aString
46
Added:
]
src/MyFitnessTracker-Components/MFTWorkoutForm.class.st
@@ -0,0 +1,45 @@
1
Added:
Class {
2
Added:
#name : 'MFTWorkoutForm',
3
Added:
#superclass : 'MFTForm',
4
Added:
#category : 'MyFitnessTracker-Components-Forms',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Forms'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTWorkoutForm class >> edit: aMFTWorkout [
11
Added:
12
Added:
^ self new
13
Added:
modelObject: aMFTWorkout;
14
Added:
yourself
15
Added:
]
16
Added:
17
Added:
{ #category : 'initialization' }
18
Added:
MFTWorkoutForm >> initialize [
19
Added:
20
Added:
super initialize.
21
Added:
modelObject := MFTWorkout new.
22
Added:
heading := 'Workout'
23
Added:
]
24
Added:
25
Added:
{ #category : 'rendering' }
26
Added:
MFTWorkoutForm >> renderContentOn: html [
27
Added:
28
Added:
self renderFormOn: html with: [
29
Added:
html
30
Added:
render: (MFTFormComponent set: #date);
31
Added:
render: (MFTFormComponent set: #setGroups);
32
Added:
render: (MFTFormComponent set: #primaryMuscleGroup);
33
Added:
render: (MFTFormComponent set: #secondaryMuscleGroups) ]
34
Added:
]
35
Added:
36
Added:
{ #category : 'as yet unclassified' }
37
Added:
MFTWorkoutForm >> submitForm [
38
Added:
39
Added:
modelObject setGroups ifEmpty: [
40
Added:
(self call:
41
Added:
(MFTConfirmForm new modelObject: 'No exercises were specified.'))
42
Added:
ifFalse: [ self answer: nil ] ].
43
Added:
44
Added:
self answer: modelObject
45
Added:
]
src/MyFitnessTracker-Components/MFTWorkoutTask.class.st
@@ -0,0 +1,101 @@
1
Added:
Class {
2
Added:
#name : 'MFTWorkoutTask',
3
Added:
#superclass : 'WATask',
4
Added:
#instVars : [
5
Added:
'workout',
6
Added:
'action'
7
Added:
],
8
Added:
#category : 'MyFitnessTracker-Components-Tasks',
9
Added:
#package : 'MyFitnessTracker-Components',
10
Added:
#tag : 'Tasks'
11
Added:
}
12
Added:
13
Added:
{ #category : 'actions' }
14
Added:
MFTWorkoutTask class >> edit: aMFTWorkout [
15
Added:
16
Added:
^ self new
17
Added:
workout: aMFTWorkout;
18
Added:
action: #edit;
19
Added:
yourself
20
Added:
]
21
Added:
22
Added:
{ #category : 'accessing' }
23
Added:
MFTWorkoutTask >> action [
24
Added:
25
Added:
^ action
26
Added:
]
27
Added:
28
Added:
{ #category : 'accessing' }
29
Added:
MFTWorkoutTask >> action: anObject [
30
Added:
31
Added:
action := anObject
32
Added:
]
33
Added:
34
Added:
{ #category : 'running' }
35
Added:
MFTWorkoutTask >> go [
36
Added:
37
Added:
| primaryMuscleGroup secondaryMuscleGroups |
38
Added:
workout userId: self session user id.
39
Added:
workout date: (self call: (MFTDateDialog new
40
Added:
action: action;
41
Added:
date: workout date;
42
Added:
completion: 20)).
43
Added:
workout setGroups: (self call: (MFTSetGroupsDialog new
44
Added:
action: action;
45
Added:
heading: 'Exercises';
46
Added:
setGroups: workout setGroups;
47
Added:
completion: 40)).
48
Added:
primaryMuscleGroup := self call: (MFTPrimaryMuscleGroupDialog new
49
Added:
action: action;
50
Added:
primaryMuscleGroup:
51
Added:
workout primaryMuscleGroup;
52
Added:
completion: 60).
53
Added:
primaryMuscleGroup ifNotNil: [
54
Added:
workout primaryMuscleGroupId: primaryMuscleGroup id.
55
Added:
secondaryMuscleGroups := self call:
56
Added:
(MFTSecondaryMuscleGroupsDialog new
57
Added:
action: action;
58
Added:
secondaryMuscleGroups:
59
Added:
workout secondaryMuscleGroups;
60
Added:
completion: 80).
61
Added:
workout secondaryMuscleGroupIds:
62
Added:
(secondaryMuscleGroups collect: #id) ].
63
Added:
64
Added:
(self call: (MFTConfirmDialog new
65
Added:
action: action;
66
Added:
component: workout))
67
Added:
ifTrue: [
68
Added:
self updateSetsAttributes.
69
Added:
self answer: workout ]
70
Added:
ifFalse: [ self answer: nil ]
71
Added:
]
72
Added:
73
Added:
{ #category : 'initialization' }
74
Added:
MFTWorkoutTask >> initialize [
75
Added:
76
Added:
super initialize.
77
Added:
workout := MFTWorkout new.
78
Added:
action := #add
79
Added:
]
80
Added:
81
Added:
{ #category : 'as yet unclassified' }
82
Added:
MFTWorkoutTask >> updateSetsAttributes [
83
Added:
84
Added:
workout setGroups doWithIndex: [ :setGroup :i |
85
Added:
setGroup sets doWithIndex: [ :set :j |
86
Added:
set
87
Added:
groupId: i;
88
Added:
number: j ] ]
89
Added:
]
90
Added:
91
Added:
{ #category : 'accessing' }
92
Added:
MFTWorkoutTask >> workout [
93
Added:
94
Added:
^ workout
95
Added:
]
96
Added:
97
Added:
{ #category : 'accessing' }
98
Added:
MFTWorkoutTask >> workout: anObject [
99
Added:
100
Added:
workout := anObject
101
Added:
]
src/MyFitnessTracker-Components/MFTWorkoutVolumePerWeekGraph.class.st
@@ -0,0 +1,46 @@
1
Added:
Class {
2
Added:
#name : 'MFTWorkoutVolumePerWeekGraph',
3
Added:
#superclass : 'MFTGraph',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'as yet unclassified' }
10
Added:
MFTWorkoutVolumePerWeekGraph >> crunchTheNumbers [
11
Added:
"TODO: not working at all!"
12
Added:
13
Added:
| datasets datasetsAsJs |
14
Added:
labels := Week dayNames.
15
Added:
data := self session user workouts groupedBy: [ :each |
16
Added:
each date dayOfYear // 7 ].
17
Added:
"datasets := {
18
Added:
{ 1. 2. 0. 0. 3. 5. 3 }.
19
Added:
{ 3. 4. 3. 0. 0. 0. 6 } }."
20
Added:
datasetsAsJs := data keysAndValuesDo: [ :k :v |
21
Added:
'{ label: ' , (#w , k asString) asJavascript
22
Added:
, ', data: ' , (v collect: #volume) asJavascript
23
Added:
, '},' ]
24
Added:
]
25
Added:
26
Added:
{ #category : 'initialization' }
27
Added:
MFTWorkoutVolumePerWeekGraph >> initialize [
28
Added:
29
Added:
super initialize.
30
Added:
self crunchTheNumbers
31
Added:
]
32
Added:
33
Added:
{ #category : 'rendering' }
34
Added:
MFTWorkoutVolumePerWeekGraph >> renderContentOn: html [
35
Added:
36
Added:
html canvas id: 'workoutVolumePerWeekChart'.
37
Added:
html script:
38
Added:
'var ctx = document.getElementById("workoutVolumePerWeekChart").getContext("2d");
39
Added:
var weightChart = new Chart(ctx, {
40
Added:
type: "line",
41
Added:
data: {
42
Added:
labels: ' , labels asJavascript , ',
43
Added:
datasets: [ ' , data asJavascript , ' ]
44
Added:
},
45
Added:
});'
46
Added:
]
src/MyFitnessTracker-Components/MFTWorkoutVolumeVsTimeGraph.class.st
@@ -0,0 +1,31 @@
1
Added:
Class {
2
Added:
#name : 'MFTWorkoutVolumeVsTimeGraph',
3
Added:
#superclass : 'MFTGraph',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'rendering' }
10
Added:
MFTWorkoutVolumeVsTimeGraph >> renderContentOn: html [
11
Added:
12
Added:
| workouts |
13
Added:
workouts := self session user workouts sort: [ :a :b |
14
Added:
a date < b date ].
15
Added:
labels := workouts collect: [ :workout | workout date yyyymmdd ].
16
Added:
data := workouts collect: #volume.
17
Added:
18
Added:
html canvas id: 'workoutVolumeVsTimeChart'.
19
Added:
html script:
20
Added:
'var ctx = document.getElementById("workoutVolumeVsTimeChart").getContext("2d");
21
Added:
var weightChart = new Chart(ctx, {
22
Added:
type: "line",
23
Added:
data: {
24
Added:
labels: ' , labels asJavascript , ',
25
Added:
datasets: [{
26
Added:
label: "total workout volume",
27
Added:
data: ' , data asJavascript , ',
28
Added:
}]
29
Added:
},
30
Added:
});'
31
Added:
]
src/MyFitnessTracker-Components/MFTWorkoutsCard.class.st
@@ -0,0 +1,53 @@
1
Added:
Class {
2
Added:
#name : 'MFTWorkoutsCard',
3
Added:
#superclass : 'MFTCard',
4
Added:
#category : 'MyFitnessTracker-Components-Widgets',
5
Added:
#package : 'MyFitnessTracker-Components',
6
Added:
#tag : 'Widgets'
7
Added:
}
8
Added:
9
Added:
{ #category : 'adding' }
10
Added:
MFTWorkoutsCard >> addWorkout [
11
Added:
12
Added:
| newWorkout |
13
Added:
newWorkout := self session pageManager activePage call:
14
Added:
MFTWorkoutTask new.
15
Added:
newWorkout ifNil: [ ^ self ].
16
Added:
17
Added:
self session user isLoggedIn
18
Added:
ifTrue: [ self session db insert: newWorkout ]
19
Added:
ifFalse: [ self session user workouts add: newWorkout ]
20
Added:
]
21
Added:
22
Added:
{ #category : 'rendering' }
23
Added:
MFTWorkoutsCard >> renderContentOn: html [
24
Added:
25
Added:
self renderCardOn: html with: [
26
Added:
html cardHeader: 'Workouts'.
27
Added:
html cardBody: [
28
Added:
html paragraph:
29
Added:
'Great job logging ' , self session user workouts size asString
30
Added:
, ' workouts!'.
31
Added:
html form
32
Added:
class: 'my-3';
33
Added:
with: [
34
Added:
html buttonGroup
35
Added:
beSmall;
36
Added:
with: [
37
Added:
html outlineButton
38
Added:
bePrimary;
39
Added:
callback: [ self addWorkout ];
40
Added:
with: 'Log workout'.
41
Added:
html outlineButton
42
Added:
beSecondary;
43
Added:
callback: [ self viewAnalytics ];
44
Added:
with: 'View progress' ] ].
45
Added:
html render: MFTRepsDistributionGraph new ] ]
46
Added:
]
47
Added:
48
Added:
{ #category : 'adding' }
49
Added:
MFTWorkoutsCard >> viewAnalytics [
50
Added:
51
Added:
self session pageManager activePage call:
52
Added:
(MFTAnalyticsPage for: #workouts)
53
Added:
]
src/MyFitnessTracker-Components/MFTWorkoutsLog.class.st
@@ -0,0 +1,172 @@
1
Added:
"
2
Added:
I represent the list of user workouts retrieved from the database.
3
Added:
4
Added:
I am rendered by `MFTRootComponent` when the user views their workouts. I know about user workouts retrieved from the database.
5
Added:
6
Added:
* Collaborators
7
Added:
8
Added:
- I work with lists of `MFTWorkout`s.
9
Added:
- I might work with `MFTExercise class`canonical exercises once the variable is implemented.
10
Added:
11
Added:
Public API and Key Messages
12
Added:
13
Added:
- message one
14
Added:
- message two
15
Added:
- (for bonus points) how to create instances.
16
Added:
17
Added:
One simple example is simply gorgeous.
18
Added:
19
Added:
Internal Representation and Key Implementation Points.
20
Added:
21
Added:
22
Added:
Implementation Points
23
Added:
"
24
Added:
Class {
25
Added:
#name : 'MFTWorkoutsLog',
26
Added:
#superclass : 'MFTLog',
27
Added:
#instVars : [
28
Added:
'workouts'
29
Added:
],
30
Added:
#category : 'MyFitnessTracker-Components-Logs',
31
Added:
#package : 'MyFitnessTracker-Components',
32
Added:
#tag : 'Logs'
33
Added:
}
34
Added:
35
Added:
{ #category : 'adding' }
36
Added:
MFTWorkoutsLog >> addWorkout [
37
Added:
38
Added:
| newWorkout |
39
Added:
newWorkout := self call: MFTWorkoutForm new.
40
Added:
newWorkout ifNil: [ ^ self ].
41
Added:
42
Added:
self session user isLoggedIn
43
Added:
ifTrue: [ self session db insert: newWorkout ]
44
Added:
ifFalse: [ workouts add: newWorkout ]
45
Added:
]
46
Added:
47
Added:
{ #category : 'hooks' }
48
Added:
MFTWorkoutsLog >> children [
49
Added:
50
Added:
^ self session user workouts
51
Added:
]
52
Added:
53
Added:
{ #category : 'accessing' }
54
Added:
MFTWorkoutsLog >> confirmDelete: workout [
55
Added:
56
Added:
(self call: (MFTConfirmForm delete)) ifFalse: [ ^ self ].
57
Added:
self session user isLoggedIn
58
Added:
ifTrue: [ self session db delete: workout ]
59
Added:
ifFalse: [ workouts remove: workout ]
60
Added:
]
61
Added:
62
Added:
{ #category : 'as yet unclassified' }
63
Added:
MFTWorkoutsLog >> editWorkout: oldWorkout [
64
Added:
65
Added:
| newWorkout |
66
Added:
newWorkout := self call: (MFTWorkoutForm edit: oldWorkout copy).
67
Added:
newWorkout ifNil: [ ^ self ].
68
Added:
69
Added:
self session user isLoggedIn
70
Added:
ifTrue: [ self session db update: oldWorkout with: newWorkout ]
71
Added:
ifFalse: [
72
Added:
workouts
73
Added:
remove: oldWorkout;
74
Added:
add: newWorkout ]
75
Added:
]
76
Added:
77
Added:
{ #category : 'initialization' }
78
Added:
MFTWorkoutsLog >> initialize [
79
Added:
80
Added:
super initialize.
81
Added:
title := 'workouts'.
82
Added:
workouts := OrderedCollection new
83
Added:
]
84
Added:
85
Added:
{ #category : 'as yet unclassified' }
86
Added:
MFTWorkoutsLog >> randomWittyCTA [
87
Added:
88
Added:
| listOfStrings randomString |
89
Added:
listOfStrings := #( 'First time?' 'Get your sweat on!'
90
Added:
'Go go go!' 'Go get ''em, Tiger!' ).
91
Added:
randomString := listOfStrings atRandom.
92
Added:
^ randomString
93
Added:
]
94
Added:
95
Added:
{ #category : 'rendering' }
96
Added:
MFTWorkoutsLog >> renderActionsButtonGroupOn: html [
97
Added:
98
Added:
html form: [
99
Added:
html buttonGroup: [
100
Added:
html formButton
101
Added:
bePrimary;
102
Added:
callback: [ self addWorkout ];
103
Added:
with: 'Log workout'.
104
Added:
html formButton
105
Added:
beSecondary;
106
Added:
callback: [ self viewAnalytics ];
107
Added:
with: 'View progress' ] ]
108
Added:
]
109
Added:
110
Added:
{ #category : 'rendering' }
111
Added:
MFTWorkoutsLog >> renderContentOn: html [
112
Added:
113
Added:
workouts := self session user workouts sort: [ :a :b |
114
Added:
a date > b date ].
115
Added:
self renderLogOn: html with: [
116
Added:
workouts ifEmpty: [
117
Added:
html jumbotron: [ html heading: self randomWittyCTA ] ].
118
Added:
self renderActionsButtonGroupOn: html.
119
Added:
workouts ifNotEmpty: [ self renderWorkoutsAsAccordionOn: html ] ]
120
Added:
]
121
Added:
122
Added:
{ #category : 'rendering' }
123
Added:
MFTWorkoutsLog >> renderWorkoutActionsButtonGroup: html for: workout [
124
Added:
125
Added:
html form: [
126
Added:
html buttonGroup: [
127
Added:
html formButton
128
Added:
bePrimary;
129
Added:
callback: [ self editWorkout: workout ];
130
Added:
with: 'Edit'.
131
Added:
html formButton
132
Added:
beDanger;
133
Added:
callback: [ self confirmDelete: workout ];
134
Added:
with: 'Delete' ] ]
135
Added:
]
136
Added:
137
Added:
{ #category : 'rendering' }
138
Added:
MFTWorkoutsLog >> renderWorkoutsAsAccordionOn: html [
139
Added:
140
Added:
html accordion
141
Added:
id: 'accordion';
142
Added:
class: 'my-3';
143
Added:
with: [
144
Added:
workouts doWithIndex: [ :workout :i |
145
Added:
html accordionItem: [
146
Added:
html accordionHeading: [
147
Added:
html accordionButton
148
Added:
dataToggle: 'collapse';
149
Added:
dataTarget: '#collapse' , i asString;
150
Added:
with: [
151
Added:
html text:
152
Added:
workout date yyyymmdd , ', ' , workout date dayOfWeekName.
153
Added:
workout primaryMuscleGroup ifNotNil: [
154
Added:
html
155
Added:
text: ':';
156
Added:
space;
157
Added:
emphasis: workout primaryMuscleGroup englishName ] ] ].
158
Added:
html accordionCollapse
159
Added:
id: 'collapse' , i asString;
160
Added:
class: 'collapse';
161
Added:
dataAttributeAt: 'bs-parent' put: '#accordion';
162
Added:
with: [
163
Added:
html accordionBody with: [
164
Added:
html render: workout.
165
Added:
self renderWorkoutActionsButtonGroup: html for: workout ] ] ] ] ]
166
Added:
]
167
Added:
168
Added:
{ #category : 'adding' }
169
Added:
MFTWorkoutsLog >> viewAnalytics [
170
Added:
171
Added:
self call: (MFTAnalyticsPage for: #workouts)
172
Added:
]
src/MyFitnessTracker-Components/package.st
@@ -0,0 +1,1 @@
1
Added:
Package { #name : 'MyFitnessTracker-Components' }
src/MyFitnessTracker/MFTAboutPage.class.st
@@ -1,87 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTAboutPage',
3
Removed:
#superclass : 'MFTPage',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'initialization' }
10
Removed:
MFTAboutPage >> initialize [
11
Removed:
12
Removed:
super initialize.
13
Removed:
title := 'about'
14
Removed:
]
15
Removed:
16
Removed:
{ #category : 'rendering' }
17
Removed:
MFTAboutPage >> renderBackgroundOn: html [
18
Removed:
19
Removed:
html heading level1 with: 'Background'.
20
Removed:
html heading level2 with: 'Why I built this web app'.
21
Removed:
html
22
Removed:
paragraph:
23
Removed:
'I wanted a convenient way to record, perform data analysis, and create visualizations for my workouts.';
24
Removed:
paragraph:
25
Removed:
'The following features were, and still are, essential for me:';
26
Removed:
unorderedList: [
27
Removed:
html
28
Removed:
listItem: 'Free access to all app features,';
29
Removed:
listItem: 'Easy import and export of my training data,';
30
Removed:
listItem:
31
Removed:
'An extensible interface to suit a wide range of training routines.' ];
32
Removed:
paragraph:
33
Removed:
'What better way than to build my own Personal Fitness Tracker?!'.
34
Removed:
html heading level2 with: 'Who I am'.
35
Removed:
html paragraph:
36
Removed:
'I am an engineer working in Europe, with a deep passion for health and fitness. Publishing My Fitness Tracker is a way for me to give thanks to everyone who has inspired me throughtout my fitness journey.'
37
Removed:
]
38
Removed:
39
Removed:
{ #category : 'rendering' }
40
Removed:
MFTAboutPage >> renderCatchOn: html [
41
Removed:
42
Removed:
html heading level1 dangerText with: 'The catch'.
43
Removed:
html
44
Removed:
paragraph:
45
Removed:
'I''ve made some debatable compromises when it comes to My Fitness Tracker:';
46
Removed:
unorderedList: [
47
Removed:
html
48
Removed:
listItem: 'No external advertisement,';
49
Removed:
listItem:
50
Removed:
'No cookies or personally identifying information stored,';
51
Removed:
listItem:
52
Removed:
'No paid feature, except for the storage of your training data beyond ten workouts.' ];
53
Removed:
paragraph:
54
Removed:
'I know these design choices might frustrate and maybe even anger certain users; '
55
Removed:
,
56
Removed:
'thankfully, all previously existing fitness tracking services do not abide by these design principles.';
57
Removed:
paragraph:
58
Removed:
'I am developing My Fitness Tracker for my own personal use case, first and foremost; '
59
Removed:
,
60
Removed:
'secondly, for the benefit of as many other fitness enthusiasts as possible; '
61
Removed:
,
62
Removed:
'thirdly, for the enjoyment that developing this web application brings me. '
63
Removed:
,
64
Removed:
'The paid feature enables me to increase the amount of fitness enthusiasts I can cater to.'
65
Removed:
]
66
Removed:
67
Removed:
{ #category : 'rendering' }
68
Removed:
MFTAboutPage >> renderContentOn: html [
69
Removed:
70
Removed:
self renderPageOn: html with: [
71
Removed:
self renderVisionOn: html.
72
Removed:
self renderBackgroundOn: html.
73
Removed:
self renderCatchOn: html ]
74
Removed:
]
75
Removed:
76
Removed:
{ #category : 'rendering' }
77
Removed:
MFTAboutPage >> renderVisionOn: html [
78
Removed:
79
Removed:
html heading level1 with: 'The Vision'.
80
Removed:
html paragraph: [
81
Removed:
html text: 'Free, and eventually '.
82
Removed:
html anchor
83
Removed:
url: 'https://fsfe.org/index.en.html';
84
Removed:
with: 'libre'.
85
Removed:
html text:
86
Removed:
' fitness tracking for all to enjoy. No user data tracking and reselling; no features blocked for any reason.' ]
87
Removed:
]
src/MyFitnessTracker/MFTActionsCard.class.st
@@ -1,18 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTActionsCard',
3
Removed:
#superclass : 'MFTCard',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'rendering' }
10
Removed:
MFTActionsCard >> renderContentOn: html [
11
Removed:
12
Removed:
self renderCardOn: html with: [
13
Removed:
html form: [
14
Removed:
html formButton
15
Removed:
formControl;
16
Removed:
callback: [ MFTWorkoutTask new ];
17
Removed:
with: 'Log Workout' ] ]
18
Removed:
]
src/MyFitnessTracker/MFTAnalyticsPage.class.st
@@ -1,70 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTAnalyticsPage',
3
Removed:
#superclass : 'MFTPage',
4
Removed:
#instVars : [
5
Removed:
'type'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'instance creation' }
13
Removed:
MFTAnalyticsPage class >> for: aSymbol [
14
Removed:
15
Removed:
^ self new
16
Removed:
type: aSymbol;
17
Removed:
yourself
18
Removed:
]
19
Removed:
20
Removed:
{ #category : 'accessing' }
21
Removed:
MFTAnalyticsPage >> analytics [
22
Removed:
23
Removed:
^ Dictionary newFrom: {
24
Removed:
(#workouts -> {
25
Removed:
MFTRepsDistributionGraph new.
26
Removed:
MFTWorkoutVolumeVsTimeGraph new
27
Removed:
"MFTWorkoutVolumePerWeekGraph new" }).
28
Removed:
(#weights -> { MFTWeightVsTimeGraph new }) }
29
Removed:
]
30
Removed:
31
Removed:
{ #category : 'rendering' }
32
Removed:
MFTAnalyticsPage >> renderActionsButtonGroupOn: html [
33
Removed:
34
Removed:
html form class: 'my-3'; with: [
35
Removed:
html buttonGroup: [
36
Removed:
html formButton
37
Removed:
bePrimary;
38
Removed:
callback: [ self answer ];
39
Removed:
with: 'Return' ] ]
40
Removed:
]
41
Removed:
42
Removed:
{ #category : 'rendering' }
43
Removed:
MFTAnalyticsPage >> renderContentOn: html [
44
Removed:
45
Removed:
| components |
46
Removed:
components := self analytics at: type.
47
Removed:
48
Removed:
self renderPageOn: html with: [
49
Removed:
self renderActionsButtonGroupOn: html.
50
Removed:
html div
51
Removed:
style:
52
Removed:
'display: flex; flex-wrap: wrap; justify-content: space-around; gap: 1em';
53
Removed:
with: [
54
Removed:
components do: [ :each |
55
Removed:
html card: [
56
Removed:
html cardHeader: each title.
57
Removed:
html render: each ] ] ] ]
58
Removed:
]
59
Removed:
60
Removed:
{ #category : 'accessing' }
61
Removed:
MFTAnalyticsPage >> type [
62
Removed:
63
Removed:
^ type
64
Removed:
]
65
Removed:
66
Removed:
{ #category : 'accessing' }
67
Removed:
MFTAnalyticsPage >> type: anObject [
68
Removed:
69
Removed:
type := anObject
70
Removed:
]
src/MyFitnessTracker/MFTArticle.class.st
@@ -1,29 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTArticle',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'title'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'rendering' }
13
Removed:
MFTArticle >> renderArticleOn: html with: aBlock [
14
Removed:
15
Removed:
html heading: title capitalized.
16
Removed:
html render: aBlock
17
Removed:
]
18
Removed:
19
Removed:
{ #category : 'accessing' }
20
Removed:
MFTArticle >> title [
21
Removed:
22
Removed:
^ title
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'accessing' }
26
Removed:
MFTArticle >> title: aString [
27
Removed:
28
Removed:
title := aString
29
Removed:
]
src/MyFitnessTracker/MFTAthletesArticle.class.st
@@ -1,26 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTAthletesArticle',
3
Removed:
#superclass : 'MFTArticle',
4
Removed:
#instVars : [
5
Removed:
'athletes'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'initialization' }
13
Removed:
MFTAthletesArticle >> initialize [
14
Removed:
15
Removed:
super initialize.
16
Removed:
title := 'athletes'.
17
Removed:
athletes := { MFTMikeMentzer new }
18
Removed:
]
19
Removed:
20
Removed:
{ #category : 'rendering' }
21
Removed:
MFTAthletesArticle >> renderContentOn: html [
22
Removed:
23
Removed:
self
24
Removed:
renderArticleOn: html
25
Removed:
with: [ athletes do: [ :athlete | athlete renderContentOn: html ] ]
26
Removed:
]
src/MyFitnessTracker/MFTCard.class.st
@@ -1,21 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTCard',
3
Removed:
#superclass : 'MFTWidget',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'rendering' }
10
Removed:
MFTCard >> renderCardOn: html with: aBlock [
11
Removed:
12
Removed:
html card: aBlock
13
Removed:
]
14
Removed:
15
Removed:
{ #category : 'rendering' }
16
Removed:
MFTCard >> renderContentOn: html [
17
Removed:
18
Removed:
self
19
Removed:
renderCardOn: html
20
Removed:
with: [ html cardBody: [ html paragraph: self defaultDescription ] ]
21
Removed:
]
src/MyFitnessTracker/MFTConfirmDialog.class.st
@@ -1,28 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTConfirmDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#category : 'MyFitnessTracker-Dialogs',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Dialogs'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'as yet unclassified' }
10
Removed:
MFTConfirmDialog >> heading [
11
Removed:
12
Removed:
^ 'You are about to {1} a {2}:' format: {
13
Removed:
action.
14
Removed:
component class }
15
Removed:
]
16
Removed:
17
Removed:
{ #category : 'as yet unclassified' }
18
Removed:
MFTConfirmDialog >> renderContentOn: html [
19
Removed:
20
Removed:
self renderDialogOn: html with: [
21
Removed:
html render: component.
22
Removed:
html label: [
23
Removed:
html text: 'Are you sure?'.
24
Removed:
action = #delete ifTrue: [
25
Removed:
html
26
Removed:
space;
27
Removed:
strong: 'This action cannot be undone.' ] ] ]
28
Removed:
]
src/MyFitnessTracker/MFTConfirmForm.class.st
@@ -1,34 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTConfirmForm',
3
Removed:
#superclass : 'MFTForm',
4
Removed:
#category : 'MyFitnessTracker-Forms',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Forms'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'submorphs - add/remove' }
10
Removed:
MFTConfirmForm class >> delete [
11
Removed:
12
Removed:
^ self new
13
Removed:
modelObject: 'Confirm deletion?';
14
Removed:
yourself
15
Removed:
]
16
Removed:
17
Removed:
{ #category : 'as yet unclassified' }
18
Removed:
MFTConfirmForm >> cancelForm [
19
Removed:
20
Removed:
self answer: false
21
Removed:
]
22
Removed:
23
Removed:
{ #category : 'initialization' }
24
Removed:
MFTConfirmForm >> initialize [
25
Removed:
26
Removed:
super initialize .
27
Removed:
heading := 'Heads up!'
28
Removed:
]
29
Removed:
30
Removed:
{ #category : 'as yet unclassified' }
31
Removed:
MFTConfirmForm >> submitForm [
32
Removed:
33
Removed:
self answer: true
34
Removed:
]
src/MyFitnessTracker/MFTDateDialog.class.st
@@ -1,50 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTDateDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'date'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'accessing' }
13
Removed:
MFTDateDialog >> date [
14
Removed:
15
Removed:
^ date
16
Removed:
]
17
Removed:
18
Removed:
{ #category : 'accessing' }
19
Removed:
MFTDateDialog >> date: anObject [
20
Removed:
21
Removed:
date := anObject
22
Removed:
]
23
Removed:
24
Removed:
{ #category : 'initialization' }
25
Removed:
MFTDateDialog >> initialize [
26
Removed:
27
Removed:
super initialize.
28
Removed:
date := Date today
29
Removed:
]
30
Removed:
31
Removed:
{ #category : 'rendering' }
32
Removed:
MFTDateDialog >> renderContentOn: html [
33
Removed:
34
Removed:
self renderDialogOn: html with: [
35
Removed:
html dateInput5 formControl
36
Removed:
value: date yyyymmdd;
37
Removed:
callback: [ :value |
38
Removed:
date := Date readFrom: value pattern: 'yyyy-mm-dd' ] ]
39
Removed:
]
40
Removed:
41
Removed:
{ #category : 'as yet unclassified' }
42
Removed:
MFTDateDialog >> renderSubmitOn: html [
43
Removed:
44
Removed:
html buttonGroup: [
45
Removed:
html formButton
46
Removed:
beSubmit;
47
Removed:
bePrimary;
48
Removed:
callback: [ self answer: date ];
49
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]) ]
50
Removed:
]
src/MyFitnessTracker/MFTDateFormComponent.class.st
@@ -1,24 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTDateFormComponent',
3
Removed:
#superclass : 'MFTFormComponent',
4
Removed:
#category : 'MyFitnessTracker-Forms',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Forms'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'initialization' }
10
Removed:
MFTDateFormComponent >> initialize [
11
Removed:
12
Removed:
super initialize.
13
Removed:
heading := 'Date'
14
Removed:
]
15
Removed:
16
Removed:
{ #category : 'rendering' }
17
Removed:
MFTDateFormComponent >> renderContentOn: html [
18
Removed:
19
Removed:
self renderFormComponentOn: html with: [
20
Removed:
html dateInput5 formControl
21
Removed:
value: modelObject date yyyymmdd;
22
Removed:
callback: [ :value |
23
Removed:
modelObject date: (Date readFrom: value pattern: 'yyyy-mm-dd') ] ]
24
Removed:
]
src/MyFitnessTracker/MFTDefaultFooter.class.st
@@ -1,19 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTDefaultFooter',
3
Removed:
#superclass : 'MFTFooter',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'rendering' }
10
Removed:
MFTDefaultFooter >> renderContentOn: html [
11
Removed:
12
Removed:
self renderFooterOn: html with: [
13
Removed:
html paragraph: [
14
Removed:
html text:
15
Removed:
'Copyright 2023–' , Date today year asString , ' Marius PETER '.
16
Removed:
html badge beSecondary with: (MFTUtils version
17
Removed:
ifNil: [ 'DEVELOP' ]
18
Removed:
ifNotNil: [ 'v' , MFTUtils version ]) ] ]
19
Removed:
]
src/MyFitnessTracker/MFTDialog.class.st
@@ -1,174 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTDialog',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'heading',
6
Removed:
'subheading',
7
Removed:
'component',
8
Removed:
'action',
9
Removed:
'completion'
10
Removed:
],
11
Removed:
#category : 'MyFitnessTracker-Dialogs',
12
Removed:
#package : 'MyFitnessTracker',
13
Removed:
#tag : 'Dialogs'
14
Removed:
}
15
Removed:
16
Removed:
{ #category : 'actions' }
17
Removed:
MFTDialog class >> add: anObject [
18
Removed:
19
Removed:
^ self new
20
Removed:
action: #add;
21
Removed:
component: anObject;
22
Removed:
yourself
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'actions' }
26
Removed:
MFTDialog class >> delete: anObject [
27
Removed:
28
Removed:
^ self new
29
Removed:
action: #delete;
30
Removed:
component: anObject;
31
Removed:
yourself
32
Removed:
]
33
Removed:
34
Removed:
{ #category : 'actions' }
35
Removed:
MFTDialog class >> edit: anObject [
36
Removed:
37
Removed:
^ self new
38
Removed:
action: #edit;
39
Removed:
component: anObject;
40
Removed:
yourself
41
Removed:
]
42
Removed:
43
Removed:
{ #category : 'accessing' }
44
Removed:
MFTDialog >> action [
45
Removed:
46
Removed:
^ action
47
Removed:
]
48
Removed:
49
Removed:
{ #category : 'accessing' }
50
Removed:
MFTDialog >> action: anObject [
51
Removed:
52
Removed:
action := anObject
53
Removed:
]
54
Removed:
55
Removed:
{ #category : 'delegation' }
56
Removed:
MFTDialog >> answer [
57
Removed:
58
Removed:
self answer: component
59
Removed:
]
60
Removed:
61
Removed:
{ #category : 'accessing' }
62
Removed:
MFTDialog >> completion [
63
Removed:
64
Removed:
^ completion
65
Removed:
]
66
Removed:
67
Removed:
{ #category : 'accessing' }
68
Removed:
MFTDialog >> completion: anObject [
69
Removed:
70
Removed:
completion := anObject
71
Removed:
]
72
Removed:
73
Removed:
{ #category : 'accessing' }
74
Removed:
MFTDialog >> component [
75
Removed:
76
Removed:
^ component
77
Removed:
]
78
Removed:
79
Removed:
{ #category : 'accessing' }
80
Removed:
MFTDialog >> component: anObject [
81
Removed:
82
Removed:
component := anObject
83
Removed:
]
84
Removed:
85
Removed:
{ #category : 't - rotating' }
86
Removed:
MFTDialog >> heading [
87
Removed:
88
Removed:
^ action capitalized , ' ' , (heading ifNil: [
89
Removed:
('^MFT(.*)Dialog$' asRegex
90
Removed:
search: self class name;
91
Removed:
subexpression: 2) asCapitalizedPhrase ifNil: [
92
Removed:
'Default Component' ] ])
93
Removed:
]
94
Removed:
95
Removed:
{ #category : 'accessing' }
96
Removed:
MFTDialog >> heading: aString [
97
Removed:
98
Removed:
heading := aString
99
Removed:
]
100
Removed:
101
Removed:
{ #category : 'initialization' }
102
Removed:
MFTDialog >> initialize [
103
Removed:
104
Removed:
super initialize.
105
Removed:
action := #add
106
Removed:
]
107
Removed:
108
Removed:
{ #category : 'rendering' }
109
Removed:
MFTDialog >> renderCompletionOn: html [
110
Removed:
111
Removed:
completion ifNotNil: [
112
Removed:
html progress
113
Removed:
class: 'my-3';
114
Removed:
with: [
115
Removed:
html progressBar
116
Removed:
beStriped;
117
Removed:
beAnimated;
118
Removed:
successBackground;
119
Removed:
valueNow: completion;
120
Removed:
with: completion percent ] ]
121
Removed:
]
122
Removed:
123
Removed:
{ #category : 'rendering' }
124
Removed:
MFTDialog >> renderContentOn: html [
125
Removed:
126
Removed:
self renderDialogOn: html with: [
127
Removed:
html text:
128
Removed:
'By default, ' , self class name , ' doesn''t do anything.' ]
129
Removed:
]
130
Removed:
131
Removed:
{ #category : 'rendering' }
132
Removed:
MFTDialog >> renderDialogOn: html with: aBlock [
133
Removed:
134
Removed:
html container
135
Removed:
style: 'max-width: 40rem';
136
Removed:
with: [
137
Removed:
html heading level1 with: self heading.
138
Removed:
subheading ifNotNil: [
139
Removed:
html heading level3 textMuted with: subheading ].
140
Removed:
html form: [
141
Removed:
aBlock value.
142
Removed:
html horizontalRule.
143
Removed:
self renderSubmitOn: html ].
144
Removed:
self renderCompletionOn: html ]
145
Removed:
]
146
Removed:
147
Removed:
{ #category : 'rendering' }
148
Removed:
MFTDialog >> renderSubmitOn: html [
149
Removed:
150
Removed:
html buttonGroup: [
151
Removed:
html formButton
152
Removed:
beSubmit;
153
Removed:
bePrimary;
154
Removed:
beDangerIf: action = #delete;
155
Removed:
callback: [ self answer: true ];
156
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
157
Removed:
html formButton
158
Removed:
beSubmit;
159
Removed:
beSecondary;
160
Removed:
callback: [ self answer: false ];
161
Removed:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
162
Removed:
]
163
Removed:
164
Removed:
{ #category : 'accessing' }
165
Removed:
MFTDialog >> subheading [
166
Removed:
167
Removed:
^ subheading
168
Removed:
]
169
Removed:
170
Removed:
{ #category : 'accessing' }
171
Removed:
MFTDialog >> subheading: aString [
172
Removed:
173
Removed:
subheading := aString
174
Removed:
]
src/MyFitnessTracker/MFTDietArticle.class.st
@@ -1,7 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTDietArticle',
3
Removed:
#superclass : 'MFTArticle',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
src/MyFitnessTracker/MFTEmailSignupDialog.class.st
@@ -1,66 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTEmailSignupDialog',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'localPart',
6
Removed:
'domain'
7
Removed:
],
8
Removed:
#category : 'MyFitnessTracker-Dialogs',
9
Removed:
#package : 'MyFitnessTracker',
10
Removed:
#tag : 'Dialogs'
11
Removed:
}
12
Removed:
13
Removed:
{ #category : 'initialization' }
14
Removed:
MFTEmailSignupDialog >> initialize [
15
Removed:
16
Removed:
super initialize.
17
Removed:
localPart := String new.
18
Removed:
domain := String new
19
Removed:
]
20
Removed:
21
Removed:
{ #category : 'rendering' }
22
Removed:
MFTEmailSignupDialog >> renderContentOn: html [
23
Removed:
24
Removed:
html div
25
Removed:
darkBackground;
26
Removed:
paddingTopAndBottom: 3;
27
Removed:
paddingLeftAndRight: 5;
28
Removed:
class: 'fixed-top';
29
Removed:
style: 'z-index: 1052';
30
Removed:
with: [
31
Removed:
html heading
32
Removed:
level3;
33
Removed:
whiteText;
34
Removed:
with: 'Get notified as soon as MFT is ready!'.
35
Removed:
html paragraph
36
Removed:
whiteText;
37
Removed:
with: [
38
Removed:
html
39
Removed:
text:
40
Removed:
'You will receive a single e-mail indicating that My Fitness Tracker is usable and accepting new user registration.';
41
Removed:
space;
42
Removed:
text:
43
Removed:
'The e-mail address provided shall be used for no other purpose whatsoever.' ].
44
Removed:
html form
45
Removed:
paddingTopAndBottom: 3;
46
Removed:
with: [
47
Removed:
html inputGroup with: [
48
Removed:
html formTextInput
49
Removed:
callback: [ :value | localPart := value ];
50
Removed:
placeholder: 'my-user'.
51
Removed:
html inputGroupAppend: [ html inputGroupText: '@' ].
52
Removed:
html formTextInput
53
Removed:
callback: [ :value | domain := value ];
54
Removed:
placeholder: 'example.com'.
55
Removed:
html inputGroupAppend: [
56
Removed:
html buttonGroup: [
57
Removed:
html formButton
58
Removed:
beSecondary;
59
Removed:
beSubmit;
60
Removed:
callback: [ self answer: localPart , '@' , domain ];
61
Removed:
with: 'Confirm'.
62
Removed:
html outlineButton
63
Removed:
beSecondary;
64
Removed:
callback: [ self answer: nil ];
65
Removed:
with: 'Cancel' ] ] ] ] ]
66
Removed:
]
src/MyFitnessTracker/MFTExercisesForm.class.st
@@ -1,46 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTExercisesForm',
3
Removed:
#superclass : 'MFTForm',
4
Removed:
#category : 'MyFitnessTracker-Forms',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Forms'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'as yet unclassified' }
10
Removed:
MFTExercisesForm class >> forExerciseCount: anInteger [
11
Removed:
12
Removed:
| exercises |
13
Removed:
exercises := OrderedCollection new: anInteger.
14
Removed:
1 to: anInteger do: [ :i |
15
Removed:
exercises add: (MFTSQLite3Database allExercises at: i) ].
16
Removed:
^ self new
17
Removed:
modelObject: exercises;
18
Removed:
yourself
19
Removed:
]
20
Removed:
21
Removed:
{ #category : 'initialization' }
22
Removed:
MFTExercisesForm >> initialize [
23
Removed:
24
Removed:
super initialize.
25
Removed:
modelObject := OrderedCollection new.
26
Removed:
heading := 'Exercises'
27
Removed:
]
28
Removed:
29
Removed:
{ #category : 'rendering' }
30
Removed:
MFTExercisesForm >> renderContentOn: html [
31
Removed:
32
Removed:
self renderFormOn: html with: [ self renderSelectExercisesOn: html ]
33
Removed:
]
34
Removed:
35
Removed:
{ #category : 'rendering' }
36
Removed:
MFTExercisesForm >> renderSelectExercisesOn: html [
37
Removed:
38
Removed:
modelObject doWithIndex: [ :selectedExercise :i |
39
Removed:
html formGroup: [
40
Removed:
html label: 'Exercise ' , i asString.
41
Removed:
html select formControl
42
Removed:
list: MFTSQLite3Database allExercises;
43
Removed:
selected: selectedExercise;
44
Removed:
callback: [ :exercise | modelObject at: i put: exercise ];
45
Removed:
labels: [ :each | each englishName ] ] ]
46
Removed:
]
src/MyFitnessTracker/MFTFooter.class.st
@@ -1,28 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTFooter',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'rendering' }
10
Removed:
MFTFooter >> renderContentOn: html [
11
Removed:
12
Removed:
self
13
Removed:
renderFooterOn: html
14
Removed:
with: [ html text: 'I am the default footer' ]
15
Removed:
]
16
Removed:
17
Removed:
{ #category : 'rendering' }
18
Removed:
MFTFooter >> renderFooterOn: html with: aBlock [
19
Removed:
20
Removed:
html div
21
Removed:
marginTop: 3;
22
Removed:
paddingTopAndBottom: 3;
23
Removed:
class: 'bg-light';
24
Removed:
with: [
25
Removed:
html container
26
Removed:
class: 'd-flex justify-content-center';
27
Removed:
with: [ aBlock value ] ]
28
Removed:
]
src/MyFitnessTracker/MFTForm.class.st
@@ -1,85 +0,0 @@
1
Removed:
"
2
Removed:
I represent a user-fillable form.
3
Removed:
4
Removed:
Upon submitting the form (""submit"" button), I return (`WAComponent>>#answer:`) the `#modelObject` with its attributes populated by the appropriate `MFTFormComponent` subclasses.
5
Removed:
Upon canceling the form (""cancel"" button), I return `nil`.
6
Removed:
"
7
Removed:
Class {
8
Removed:
#name : 'MFTForm',
9
Removed:
#superclass : 'SBSComponent',
10
Removed:
#instVars : [
11
Removed:
'modelObject',
12
Removed:
'heading'
13
Removed:
],
14
Removed:
#category : 'MyFitnessTracker-Forms',
15
Removed:
#package : 'MyFitnessTracker',
16
Removed:
#tag : 'Forms'
17
Removed:
}
18
Removed:
19
Removed:
{ #category : 'as yet unclassified' }
20
Removed:
MFTForm >> cancelForm [
21
Removed:
22
Removed:
self answer: nil
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'initialization' }
26
Removed:
MFTForm >> initialize [
27
Removed:
28
Removed:
super initialize.
29
Removed:
heading := 'Form heading'
30
Removed:
]
31
Removed:
32
Removed:
{ #category : 'accessing' }
33
Removed:
MFTForm >> modelObject [
34
Removed:
35
Removed:
^ modelObject
36
Removed:
]
37
Removed:
38
Removed:
{ #category : 'accessing' }
39
Removed:
MFTForm >> modelObject: anObject [
40
Removed:
41
Removed:
modelObject := anObject
42
Removed:
]
43
Removed:
44
Removed:
{ #category : 'rendering' }
45
Removed:
MFTForm >> renderContentOn: html [
46
Removed:
47
Removed:
self renderFormOn: html with: [ html render: modelObject ]
48
Removed:
]
49
Removed:
50
Removed:
{ #category : 'as yet unclassified' }
51
Removed:
MFTForm >> renderFormOn: html with: aBlock [
52
Removed:
53
Removed:
html container
54
Removed:
style: 'max-width: 40rem';
55
Removed:
with: [
56
Removed:
html displayHeading
57
Removed:
level: 3;
58
Removed:
with: heading.
59
Removed:
html form: [
60
Removed:
aBlock value.
61
Removed:
html horizontalRule.
62
Removed:
self renderSubmitOn: html ] ]
63
Removed:
]
64
Removed:
65
Removed:
{ #category : 'as yet unclassified' }
66
Removed:
MFTForm >> renderSubmitOn: html [
67
Removed:
68
Removed:
html buttonGroup: [
69
Removed:
html formButton
70
Removed:
beSubmit;
71
Removed:
bePrimary;
72
Removed:
callback: [ self submitForm ];
73
Removed:
with: 'Submit'.
74
Removed:
html formButton
75
Removed:
beSubmit;
76
Removed:
beSecondary;
77
Removed:
callback: [ self cancelForm ];
78
Removed:
with: 'Cancel' ]
79
Removed:
]
80
Removed:
81
Removed:
{ #category : 'as yet unclassified' }
82
Removed:
MFTForm >> submitForm [
83
Removed:
84
Removed:
self answer: modelObject
85
Removed:
]
src/MyFitnessTracker/MFTFormComponent.class.st
@@ -1,92 +0,0 @@
1
Removed:
"
2
Removed:
When instantiated with the `MFTFormComponent class>>#set:` message, I return an instance of the appropriate subclass for setting the model object attribute.
3
Removed:
"
4
Removed:
Class {
5
Removed:
#name : 'MFTFormComponent',
6
Removed:
#superclass : 'SBSComponent',
7
Removed:
#instVars : [
8
Removed:
'heading',
9
Removed:
'modelObject'
10
Removed:
],
11
Removed:
#category : 'MyFitnessTracker-Forms',
12
Removed:
#package : 'MyFitnessTracker',
13
Removed:
#tag : 'Forms'
14
Removed:
}
15
Removed:
16
Removed:
{ #category : 'as yet unclassified' }
17
Removed:
MFTFormComponent class >> formComponentFor: anAttribute [
18
Removed:
19
Removed:
| formComponents |
20
Removed:
formComponents := Dictionary newFrom: {
21
Removed:
(#date -> MFTDateFormComponent).
22
Removed:
(#setGroups -> MFTSetGroupsFormComponent).
23
Removed:
(#primaryMuscleGroup
24
Removed:
-> MFTPrimaryMuscleGroupFormComponent).
25
Removed:
(#secondaryMuscleGroups
26
Removed:
-> MFTSecondaryMuscleGroupsFormComponent) }.
27
Removed:
28
Removed:
^ formComponents at: anAttribute ifAbsent: [
29
Removed:
self error:
30
Removed:
('no appropriate {1} subclass for setting attribute "{2}"'
31
Removed:
format: {
32
Removed:
self.
33
Removed:
anAttribute }) ]
34
Removed:
]
35
Removed:
36
Removed:
{ #category : 'as yet unclassified' }
37
Removed:
MFTFormComponent class >> set: anAttribute [
38
Removed:
39
Removed:
| formComponent |
40
Removed:
formComponent := self formComponentFor: anAttribute.
41
Removed:
^ formComponent new
42
Removed:
modelObject: thisContext sender receiver modelObject;
43
Removed:
yourself
44
Removed:
]
45
Removed:
46
Removed:
{ #category : 'accessing' }
47
Removed:
MFTFormComponent >> heading [
48
Removed:
49
Removed:
^ heading
50
Removed:
]
51
Removed:
52
Removed:
{ #category : 'accessing' }
53
Removed:
MFTFormComponent >> heading: anObject [
54
Removed:
55
Removed:
heading := anObject
56
Removed:
]
57
Removed:
58
Removed:
{ #category : 'initialization' }
59
Removed:
MFTFormComponent >> initialize [
60
Removed:
61
Removed:
super initialize
62
Removed:
]
63
Removed:
64
Removed:
{ #category : 'accessing' }
65
Removed:
MFTFormComponent >> modelObject [
66
Removed:
67
Removed:
^ modelObject
68
Removed:
]
69
Removed:
70
Removed:
{ #category : 'accessing' }
71
Removed:
MFTFormComponent >> modelObject: anObject [
72
Removed:
73
Removed:
modelObject := anObject
74
Removed:
]
75
Removed:
76
Removed:
{ #category : 'rendering' }
77
Removed:
MFTFormComponent >> renderContentOn: html [
78
Removed:
79
Removed:
self renderFormComponentOn: html with: [ html render: modelObject ]
80
Removed:
]
81
Removed:
82
Removed:
{ #category : 'as yet unclassified' }
83
Removed:
MFTFormComponent >> renderFormComponentOn: html with: aBlock [
84
Removed:
85
Removed:
html formGroup
86
Removed:
class: 'my-4';
87
Removed:
with: [
88
Removed:
html paragraph
89
Removed:
heading: 3;
90
Removed:
with: [ html text: heading ].
91
Removed:
aBlock value ]
92
Removed:
]
src/MyFitnessTracker/MFTGraph.class.st
@@ -1,72 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTGraph',
3
Removed:
#superclass : 'MFTWidget',
4
Removed:
#instVars : [
5
Removed:
'labels',
6
Removed:
'data'
7
Removed:
],
8
Removed:
#category : 'MyFitnessTracker-Components',
9
Removed:
#package : 'MyFitnessTracker',
10
Removed:
#tag : 'Components'
11
Removed:
}
12
Removed:
13
Removed:
{ #category : 'accessing' }
14
Removed:
MFTGraph >> data [
15
Removed:
16
Removed:
^ data
17
Removed:
]
18
Removed:
19
Removed:
{ #category : 'accessing' }
20
Removed:
MFTGraph >> data: anObject [
21
Removed:
22
Removed:
data := anObject
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'initialization' }
26
Removed:
MFTGraph >> initialize [
27
Removed:
28
Removed:
super initialize.
29
Removed:
labels := #( Monday Tuesday Wednesday Thursday Friday ).
30
Removed:
data := #( 5 1 6 2 7 )
31
Removed:
]
32
Removed:
33
Removed:
{ #category : 'accessing' }
34
Removed:
MFTGraph >> labels [
35
Removed:
36
Removed:
^ labels
37
Removed:
]
38
Removed:
39
Removed:
{ #category : 'accessing' }
40
Removed:
MFTGraph >> labels: anObject [
41
Removed:
42
Removed:
labels := anObject
43
Removed:
]
44
Removed:
45
Removed:
{ #category : 'rendering' }
46
Removed:
MFTGraph >> renderContentOn: html [
47
Removed:
48
Removed:
html heading level5 with: self defaultDescription.
49
Removed:
html canvas id: 'defaultGraph'.
50
Removed:
html script:
51
Removed:
'var ctx = document.getElementById("defaultGraph").getContext("2d");
52
Removed:
var defaultChart = new Chart(ctx, {
53
Removed:
type: "line",
54
Removed:
data: {
55
Removed:
labels: ' , labels asJavascript , ',
56
Removed:
datasets: [{
57
Removed:
label: "default data",
58
Removed:
data: ' , data asJavascript , ',
59
Removed:
borderWidth: 1
60
Removed:
}]
61
Removed:
}
62
Removed:
});'
63
Removed:
]
64
Removed:
65
Removed:
{ #category : 'accessing' }
66
Removed:
MFTGraph >> title [
67
Removed:
68
Removed:
^ ('^MFT(.*)Graph$' asRegex
69
Removed:
search: self class name;
70
Removed:
subexpression: 2) asCapitalizedPhrase ifNil: [
71
Removed:
'Default Graph' ]
72
Removed:
]
src/MyFitnessTracker/MFTHeaderComponent.class.st
@@ -1,7 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTHeaderComponent',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
src/MyFitnessTracker/MFTLandingPage.class.st
@@ -1,55 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTLandingPage',
3
Removed:
#superclass : 'MFTPage',
4
Removed:
#instVars : [
5
Removed:
'cards'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'accessing' }
13
Removed:
MFTLandingPage >> cards [
14
Removed:
15
Removed:
^ cards
16
Removed:
]
17
Removed:
18
Removed:
{ #category : 'accessing' }
19
Removed:
MFTLandingPage >> cards: aMFTCardCollection [
20
Removed:
21
Removed:
cards := aMFTCardCollection
22
Removed:
]
23
Removed:
24
Removed:
{ #category : 'hooks' }
25
Removed:
MFTLandingPage >> children [
26
Removed:
27
Removed:
^ cards
28
Removed:
]
29
Removed:
30
Removed:
{ #category : 'as yet unclassified' }
31
Removed:
MFTLandingPage >> defaultCards [
32
Removed:
33
Removed:
^ {
34
Removed:
MFTCard new.
35
Removed:
MFTCard new.
36
Removed:
MFTCard new }
37
Removed:
]
38
Removed:
39
Removed:
{ #category : 'as yet unclassified' }
40
Removed:
MFTLandingPage >> initialize [
41
Removed:
42
Removed:
super initialize.
43
Removed:
title := 'home'.
44
Removed:
cards := self defaultCards
45
Removed:
]
46
Removed:
47
Removed:
{ #category : 'rendering' }
48
Removed:
MFTLandingPage >> renderContentOn: html [
49
Removed:
50
Removed:
self renderPageOn: html with: [
51
Removed:
html div
52
Removed:
style:
53
Removed:
'display: flex; flex-wrap: wrap; justify-content: space-around; gap: 1em';
54
Removed:
with: [ self cards do: [ :card | html render: card ] ] ]
55
Removed:
]
src/MyFitnessTracker/MFTLearnPage.class.st
@@ -1,35 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTLearnPage',
3
Removed:
#superclass : 'MFTPage',
4
Removed:
#instVars : [
5
Removed:
'articles',
6
Removed:
'activeArticle'
7
Removed:
],
8
Removed:
#category : 'MyFitnessTracker-Components',
9
Removed:
#package : 'MyFitnessTracker',
10
Removed:
#tag : 'Components'
11
Removed:
}
12
Removed:
13
Removed:
{ #category : 'initialization' }
14
Removed:
MFTLearnPage >> initialize [
15
Removed:
16
Removed:
super initialize.
17
Removed:
title := 'learn'.
18
Removed:
articles := {
19
Removed:
MFTAthletesArticle new.
20
Removed:
MFTMusclesArticle new }.
21
Removed:
activeArticle := articles first
22
Removed:
]
23
Removed:
24
Removed:
{ #category : 'rendering' }
25
Removed:
MFTLearnPage >> renderContentOn: html [
26
Removed:
27
Removed:
self renderPageOn: html with: [
28
Removed:
html navigation flexColumn with: [
29
Removed:
articles do: [ :article |
30
Removed:
html navigationLink
31
Removed:
beActiveIf: [ activeArticle = article ];
32
Removed:
callback: [ activeArticle := article ];
33
Removed:
with: article title capitalized ] ].
34
Removed:
html render: activeArticle ]
35
Removed:
]
src/MyFitnessTracker/MFTLog.class.st
@@ -1,28 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTLog',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'title'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'rendering' }
13
Removed:
MFTLog >> renderLogOn: html with: aBlock [
14
Removed:
15
Removed:
html div: aBlock
16
Removed:
]
17
Removed:
18
Removed:
{ #category : 'accessing' }
19
Removed:
MFTLog >> title [
20
Removed:
21
Removed:
^ title
22
Removed:
]
23
Removed:
24
Removed:
{ #category : 'accessing' }
25
Removed:
MFTLog >> title: anObject [
26
Removed:
27
Removed:
title := anObject
28
Removed:
]
src/MyFitnessTracker/MFTLoginRegisterLogoutComponent.class.st
@@ -1,155 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTLoginRegisterLogoutComponent',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'user'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'as yet unclassified' }
13
Removed:
MFTLoginRegisterLogoutComponent >> attemptLogin: aMFTUser [
14
Removed:
"Attempt to match aMFTUser passwordHash against an existing user password_hash in the database."
15
Removed:
16
Removed:
| existingUser |
17
Removed:
aMFTUser username = 'testMFT' ifTrue: [
18
Removed:
^ self session login: MFTSQLite3Database getTestUser ].
19
Removed:
existingUser := MFTSQLite3Database getExistingUser: aMFTUser.
20
Removed:
aMFTUser passwordHash = existingUser passwordHash
21
Removed:
ifTrue: [ self session login: existingUser ]
22
Removed:
ifFalse: [ self inform: 'Login failed' ]
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'initialization' }
26
Removed:
MFTLoginRegisterLogoutComponent >> initialize [
27
Removed:
28
Removed:
super initialize.
29
Removed:
user := MFTUser new
30
Removed:
]
31
Removed:
32
Removed:
{ #category : 'as yet unclassified' }
33
Removed:
MFTLoginRegisterLogoutComponent >> logout [
34
Removed:
35
Removed:
self session logout
36
Removed:
]
37
Removed:
38
Removed:
{ #category : 'rendering' }
39
Removed:
MFTLoginRegisterLogoutComponent >> renderContentOn: html [
40
Removed:
41
Removed:
self session user isLoggedIn
42
Removed:
ifFalse: [ self renderLoginAndRegisterModalsOn: html ]
43
Removed:
ifTrue: [ self renderLogoutButtonOn: html ]
44
Removed:
]
45
Removed:
46
Removed:
{ #category : 'rendering' }
47
Removed:
MFTLoginRegisterLogoutComponent >> renderLoginAndRegisterModalsOn: html [
48
Removed:
"Logging in and registering display modal components; they are not in a form."
49
Removed:
50
Removed:
html buttonGroup: [
51
Removed:
html outlineButton
52
Removed:
beSecondary;
53
Removed:
beSmall;
54
Removed:
dataToggle: 'modal';
55
Removed:
dataTarget: '#loginModal';
56
Removed:
with: 'Login'.
57
Removed:
html outlineButton
58
Removed:
bePrimary;
59
Removed:
beSmall;
60
Removed:
dataToggle: 'modal';
61
Removed:
dataTarget: '#registerModal';
62
Removed:
with: 'Register' ].
63
Removed:
self renderLoginModalOn: html.
64
Removed:
self renderRegisterModalOn: html
65
Removed:
]
66
Removed:
67
Removed:
{ #category : 'rendering' }
68
Removed:
MFTLoginRegisterLogoutComponent >> renderLoginModalOn: html [
69
Removed:
70
Removed:
html modal
71
Removed:
id: 'loginModal';
72
Removed:
fade;
73
Removed:
with: [
74
Removed:
html modalDialog
75
Removed:
beCentered;
76
Removed:
with: [
77
Removed:
html modalContent: [
78
Removed:
html form: [
79
Removed:
html modalHeader: [
80
Removed:
html modalTitle
81
Removed:
level5;
82
Removed:
with: 'Welcome back!'.
83
Removed:
html modalCloseButton ].
84
Removed:
html modalBody with: [
85
Removed:
html formGroup: [
86
Removed:
html label: 'Username'.
87
Removed:
html textInput formControl callback: [ :value |
88
Removed:
user username: value ] ].
89
Removed:
html formGroup: [
90
Removed:
html label: 'Password'.
91
Removed:
html passwordInput formControl callback: [ :value |
92
Removed:
user passwordHashFromString: value ] ] ].
93
Removed:
html modalFooter: [
94
Removed:
html buttonGroup: [
95
Removed:
html formButton
96
Removed:
bePrimary;
97
Removed:
callback: [ self attemptLogin: user ];
98
Removed:
with: 'Login'.
99
Removed:
html formButton
100
Removed:
beSecondary;
101
Removed:
dataDismiss: 'modal';
102
Removed:
with: 'Cancel' ] ] ] ] ] ]
103
Removed:
]
104
Removed:
105
Removed:
{ #category : 'rendering' }
106
Removed:
MFTLoginRegisterLogoutComponent >> renderLogoutButtonOn: html [
107
Removed:
"Logging out is an action; the button is in a form."
108
Removed:
109
Removed:
html form: [
110
Removed:
html outlineButton
111
Removed:
beSecondary;
112
Removed:
beSmall;
113
Removed:
callback: [ self logout ];
114
Removed:
with: 'Logout' ]
115
Removed:
]
116
Removed:
117
Removed:
{ #category : 'rendering' }
118
Removed:
MFTLoginRegisterLogoutComponent >> renderRegisterModalOn: html [
119
Removed:
120
Removed:
html modal
121
Removed:
id: 'registerModal';
122
Removed:
fade;
123
Removed:
with: [
124
Removed:
html modalDialog
125
Removed:
beCentered;
126
Removed:
with: [
127
Removed:
html modalContent: [
128
Removed:
html form: [
129
Removed:
html modalHeader: [
130
Removed:
html modalTitle
131
Removed:
level5;
132
Removed:
with: 'Welcome aboard!'.
133
Removed:
html modalCloseButton ].
134
Removed:
html modalBody with: [
135
Removed:
html formGroup: [
136
Removed:
html label: 'Username'.
137
Removed:
html textInput formControl callback: [ :value |
138
Removed:
user username: value ] ].
139
Removed:
html formGroup: [
140
Removed:
html label: 'Password'.
141
Removed:
html passwordInput formControl callback: [ :value |
142
Removed:
user passwordHashFromString: value ] ] ].
143
Removed:
html modalFooter: [
144
Removed:
html buttonGroup: [
145
Removed:
html formButton
146
Removed:
bePrimary;
147
Removed:
callback: [
148
Removed:
MFTSQLite3Database registerNewUser: user.
149
Removed:
self attemptLogin: user ];
150
Removed:
with: 'Register'.
151
Removed:
html formButton
152
Removed:
beSecondary;
153
Removed:
dataDismiss: 'modal';
154
Removed:
with: 'Cancel' ] ] ] ] ] ]
155
Removed:
]
src/MyFitnessTracker/MFTLogsPage.class.st
@@ -1,49 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTLogsPage',
3
Removed:
#superclass : 'MFTPage',
4
Removed:
#instVars : [
5
Removed:
'activeLog',
6
Removed:
'logs'
7
Removed:
],
8
Removed:
#category : 'MyFitnessTracker-Components',
9
Removed:
#package : 'MyFitnessTracker',
10
Removed:
#tag : 'Components'
11
Removed:
}
12
Removed:
13
Removed:
{ #category : 'hooks' }
14
Removed:
MFTLogsPage >> children [
15
Removed:
16
Removed:
^ logs
17
Removed:
]
18
Removed:
19
Removed:
{ #category : 'initialization' }
20
Removed:
MFTLogsPage >> initialize [
21
Removed:
22
Removed:
super initialize.
23
Removed:
title := 'logs'.
24
Removed:
logs := {
25
Removed:
MFTWorkoutsLog new.
26
Removed:
MFTWeightsLog new }.
27
Removed:
activeLog := logs first
28
Removed:
]
29
Removed:
30
Removed:
{ #category : 'rendering' }
31
Removed:
MFTLogsPage >> renderContentOn: html [
32
Removed:
33
Removed:
self renderPageOn: html with: [
34
Removed:
self renderLogNavigationOn: html.
35
Removed:
html render: activeLog ]
36
Removed:
]
37
Removed:
38
Removed:
{ #category : 'rendering' }
39
Removed:
MFTLogsPage >> renderLogNavigationOn: html [
40
Removed:
41
Removed:
html form
42
Removed:
class: 'mb-3';
43
Removed:
with: [
44
Removed:
html navigation flexColumn with: [
45
Removed:
logs do: [ :log |
46
Removed:
html navigationLink
47
Removed:
callback: [ activeLog := log ];
48
Removed:
with: log title ] ] ]
49
Removed:
]
src/MyFitnessTracker/MFTMainNavigationBarComponent.class.st
@@ -1,65 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTMainNavigationBarComponent',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'loginRegisterLogout'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'hooks' }
13
Removed:
MFTMainNavigationBarComponent >> children [
14
Removed:
15
Removed:
^ { loginRegisterLogout }
16
Removed:
]
17
Removed:
18
Removed:
{ #category : 'initialization' }
19
Removed:
MFTMainNavigationBarComponent >> initialize [
20
Removed:
21
Removed:
super initialize.
22
Removed:
loginRegisterLogout := MFTLoginRegisterLogoutComponent new
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'rendering' }
26
Removed:
MFTMainNavigationBarComponent >> renderContentOn: html [
27
Removed:
28
Removed:
html navigationBar
29
Removed:
beLight;
30
Removed:
lightBackground;
31
Removed:
expandLarge;
32
Removed:
marginBottom: 3;
33
Removed:
paddingLeftAndRight: 3;
34
Removed:
style: 'position: sticky; top: 0; z-index: 1051';
35
Removed:
with: [ self renderNavigationBarItemsOn: html ]
36
Removed:
]
37
Removed:
38
Removed:
{ #category : 'rendering' }
39
Removed:
MFTMainNavigationBarComponent >> renderNavigationBarItemsOn: html [
40
Removed:
41
Removed:
| pm |
42
Removed:
pm := self session pageManager.
43
Removed:
html navigationBarBrand
44
Removed:
callback: [ pm setActivePageAt: #home ];
45
Removed:
with: 'MyFitnessTracker'.
46
Removed:
"The toggler is only visible when the screen width is reduced."
47
Removed:
html navigationBarToggler
48
Removed:
beButtonType;
49
Removed:
collapse;
50
Removed:
dataTarget: '#navbarCollapsed';
51
Removed:
with: [ html navigationBarTogglerIcon ].
52
Removed:
html navigationBarCollapse collapse
53
Removed:
id: 'navbarCollapsed';
54
Removed:
class: 'justify-content-between';
55
Removed:
with: [
56
Removed:
html navigationBarNavigation: [
57
Removed:
pm mainPages keysAndValuesDo: [ :name :page |
58
Removed:
html navigationItem
59
Removed:
beActiveIf: [ pm activePage = page ];
60
Removed:
with: [
61
Removed:
html navigationLink
62
Removed:
callback: [ pm setActivePageAt: name ];
63
Removed:
with: page title capitalized ] ] ].
64
Removed:
html render: loginRegisterLogout ]
65
Removed:
]
src/MyFitnessTracker/MFTMainPublicCard.class.st
@@ -1,45 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTMainPublicCard',
3
Removed:
#superclass : 'MFTCard',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'adding' }
10
Removed:
MFTMainPublicCard >> addWorkout [
11
Removed:
12
Removed:
| newWorkout |
13
Removed:
newWorkout := self session pageManager activePage call:
14
Removed:
MFTWorkoutForm new.
15
Removed:
newWorkout
16
Removed:
ifNil: [ ^ self ]
17
Removed:
ifNotNil: [ self session pageManager setActivePageAt: #logs ].
18
Removed:
19
Removed:
self session user isLoggedIn
20
Removed:
ifTrue: [ self session db insert: newWorkout ]
21
Removed:
ifFalse: [ self session user workouts add: newWorkout ]
22
Removed:
]
23
Removed:
24
Removed:
{ #category : 'rendering' }
25
Removed:
MFTMainPublicCard >> renderContentOn: html [
26
Removed:
27
Removed:
self renderCardOn: html with: [
28
Removed:
html jumbotron
29
Removed:
class: 'p-3 bg-light h-100';
30
Removed:
with: [
31
Removed:
html displayHeading
32
Removed:
level: 1;
33
Removed:
with: 'Log, plan, share your workouts.'.
34
Removed:
html heading
35
Removed:
level: 2;
36
Removed:
textMuted;
37
Removed:
with: 'Free Software never looked so fit!'.
38
Removed:
html form: [
39
Removed:
html formButton
40
Removed:
bePrimary;
41
Removed:
beLarge;
42
Removed:
class: 'mt-3';
43
Removed:
callback: [ self addWorkout ];
44
Removed:
with: 'Log a workout' ] ] ]
45
Removed:
]
src/MyFitnessTracker/MFTMainUserCard.class.st
@@ -1,41 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTMainUserCard',
3
Removed:
#superclass : 'MFTCard',
4
Removed:
#instVars : [
5
Removed:
'lastLoginDate',
6
Removed:
'subtitle'
7
Removed:
],
8
Removed:
#category : 'MyFitnessTracker-Components',
9
Removed:
#package : 'MyFitnessTracker',
10
Removed:
#tag : 'Components'
11
Removed:
}
12
Removed:
13
Removed:
{ #category : 'initialization' }
14
Removed:
MFTMainUserCard >> initialize [
15
Removed:
16
Removed:
super initialize.
17
Removed:
lastLoginDate := self session user lastLoginDateAndTime.
18
Removed:
subtitle := 'Last logged in {1}, {2} at {3} UTC' format: {
19
Removed:
lastLoginDate dayOfWeekName.
20
Removed:
lastLoginDate asDate asString.
21
Removed:
lastLoginDate asTimeUTC asString }
22
Removed:
]
23
Removed:
24
Removed:
{ #category : 'rendering' }
25
Removed:
MFTMainUserCard >> renderContentOn: html [
26
Removed:
27
Removed:
| user |
28
Removed:
user := self session user.
29
Removed:
self renderCardOn: html with: [
30
Removed:
html jumbotron
31
Removed:
class: 'p-3 bg-light';
32
Removed:
with: [
33
Removed:
html displayHeading
34
Removed:
level: 1;
35
Removed:
with: [
36
Removed:
html
37
Removed:
text: 'Welcome, ';
38
Removed:
code: (user realFirstName ifEmpty: [ user username ]);
39
Removed:
text: '!' ].
40
Removed:
html big textMuted with: subtitle ] ]
41
Removed:
]
src/MyFitnessTracker/MFTMusclesArticle.class.st
@@ -1,31 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTMusclesArticle',
3
Removed:
#superclass : 'MFTArticle',
4
Removed:
#instVars : [
5
Removed:
'muscleGroups'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'hooks' }
13
Removed:
MFTMusclesArticle >> children [
14
Removed:
15
Removed:
^ muscleGroups
16
Removed:
]
17
Removed:
18
Removed:
{ #category : 'initialization' }
19
Removed:
MFTMusclesArticle >> initialize [
20
Removed:
21
Removed:
super initialize.
22
Removed:
title := 'muscles'
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'rendering' }
26
Removed:
MFTMusclesArticle >> renderContentOn: html [
27
Removed:
28
Removed:
self renderArticleOn: html with: [
29
Removed:
MFTSQLite3Database allMuscleGroups do: [ :muscleGroup |
30
Removed:
html render: muscleGroup ] ]
31
Removed:
]
src/MyFitnessTracker/MFTPage.class.st
@@ -1,24 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTPage',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'title'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'rendering' }
13
Removed:
MFTPage >> renderPageOn: html with: aBlock [
14
Removed:
15
Removed:
html container
16
Removed:
style: 'max-width: 50rem';
17
Removed:
with: aBlock
18
Removed:
]
19
Removed:
20
Removed:
{ #category : 'accessing' }
21
Removed:
MFTPage >> title [
22
Removed:
23
Removed:
^ title
24
Removed:
]
src/MyFitnessTracker/MFTPrimaryMuscleGroupDialog.class.st
@@ -1,64 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTPrimaryMuscleGroupDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'primaryMuscleGroup'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'initialization' }
13
Removed:
MFTPrimaryMuscleGroupDialog >> initialize [
14
Removed:
15
Removed:
super initialize.
16
Removed:
primaryMuscleGroup := MFTMuscleGroup new.
17
Removed:
18
Removed:
]
19
Removed:
20
Removed:
{ #category : 'accessing' }
21
Removed:
MFTPrimaryMuscleGroupDialog >> primaryMuscleGroup [
22
Removed:
23
Removed:
^ primaryMuscleGroup
24
Removed:
]
25
Removed:
26
Removed:
{ #category : 'accessing' }
27
Removed:
MFTPrimaryMuscleGroupDialog >> primaryMuscleGroup: anObject [
28
Removed:
29
Removed:
primaryMuscleGroup := anObject
30
Removed:
]
31
Removed:
32
Removed:
{ #category : 'rendering' }
33
Removed:
MFTPrimaryMuscleGroupDialog >> renderContentOn: html [
34
Removed:
35
Removed:
| allMuscleGroups selectedMuscleGroup |
36
Removed:
allMuscleGroups := MFTSQLite3Database allMuscleGroups.
37
Removed:
selectedMuscleGroup := primaryMuscleGroup ifNotNil: [
38
Removed:
allMuscleGroups detect: [ :muscleGroup |
39
Removed:
muscleGroup englishName
40
Removed:
= primaryMuscleGroup englishName ] ].
41
Removed:
self renderDialogOn: html with: [
42
Removed:
html formGroup: [
43
Removed:
html select formControl
44
Removed:
list: allMuscleGroups;
45
Removed:
selected: selectedMuscleGroup;
46
Removed:
labels: [ :muscleGroup | muscleGroup englishName ];
47
Removed:
callback: [ :group | primaryMuscleGroup := group ] ] ]
48
Removed:
]
49
Removed:
50
Removed:
{ #category : 'rendering' }
51
Removed:
MFTPrimaryMuscleGroupDialog >> renderSubmitOn: html [
52
Removed:
53
Removed:
html buttonGroup: [
54
Removed:
html formButton
55
Removed:
beSubmit;
56
Removed:
bePrimary;
57
Removed:
callback: [ self answer: primaryMuscleGroup ];
58
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
59
Removed:
html formButton
60
Removed:
beSubmit;
61
Removed:
beSecondary;
62
Removed:
callback: [ self answer: nil ];
63
Removed:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
64
Removed:
]
src/MyFitnessTracker/MFTPrimaryMuscleGroupFormComponent.class.st
@@ -1,32 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTPrimaryMuscleGroupFormComponent',
3
Removed:
#superclass : 'MFTFormComponent',
4
Removed:
#category : 'MyFitnessTracker-Forms',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Forms'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'initialization' }
10
Removed:
MFTPrimaryMuscleGroupFormComponent >> initialize [
11
Removed:
12
Removed:
super initialize.
13
Removed:
heading := 'Primary Group'
14
Removed:
]
15
Removed:
16
Removed:
{ #category : 'rendering' }
17
Removed:
MFTPrimaryMuscleGroupFormComponent >> renderContentOn: html [
18
Removed:
19
Removed:
| allMuscleGroups selectedMuscleGroup |
20
Removed:
allMuscleGroups := MFTSQLite3Database allMuscleGroups.
21
Removed:
selectedMuscleGroup := modelObject primaryMuscleGroup ifNotNil: [
22
Removed:
allMuscleGroups detect: [ :muscleGroup |
23
Removed:
muscleGroup englishName
24
Removed:
= modelObject primaryMuscleGroup englishName ] ].
25
Removed:
26
Removed:
self renderFormComponentOn: html with: [
27
Removed:
html select formControl
28
Removed:
list: allMuscleGroups;
29
Removed:
selected: selectedMuscleGroup;
30
Removed:
labels: [ :muscleGroup | muscleGroup englishName ];
31
Removed:
callback: [ :group | modelObject primaryMuscleGroup: group ] ]
32
Removed:
]
src/MyFitnessTracker/MFTProfilePage.class.st
@@ -1,50 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTProfilePage',
3
Removed:
#superclass : 'MFTPage',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'as yet unclassified' }
10
Removed:
MFTProfilePage >> editProfile [
11
Removed:
12
Removed:
| updatedUser |
13
Removed:
updatedUser := self call: (MFTUserTask edit: self session user).
14
Removed:
updatedUser ifNil: [ ^ self ].
15
Removed:
self session user: updatedUser.
16
Removed:
self session user isLoggedIn ifTrue: [
17
Removed:
self session db updateUserWith: updatedUser ]
18
Removed:
]
19
Removed:
20
Removed:
{ #category : 'initialization' }
21
Removed:
MFTProfilePage >> initialize [
22
Removed:
23
Removed:
super initialize.
24
Removed:
title := 'profile'
25
Removed:
]
26
Removed:
27
Removed:
{ #category : 'rendering' }
28
Removed:
MFTProfilePage >> renderContentOn: html [
29
Removed:
30
Removed:
| user |
31
Removed:
user := self session user.
32
Removed:
33
Removed:
self renderPageOn: html with: [
34
Removed:
html heading level1 with: [
35
Removed:
html
36
Removed:
code: self session user username;
37
Removed:
text: '''s profile' ].
38
Removed:
self renderProfileActionsOn: html.
39
Removed:
html render: user ]
40
Removed:
]
41
Removed:
42
Removed:
{ #category : 'rendering' }
43
Removed:
MFTProfilePage >> renderProfileActionsOn: html [
44
Removed:
45
Removed:
html form: [
46
Removed:
html formButton
47
Removed:
bePrimary;
48
Removed:
callback: [ self editProfile ];
49
Removed:
with: 'Edit profile' ]
50
Removed:
]
src/MyFitnessTracker/MFTPromisesCard.class.st
@@ -1,30 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTPromisesCard',
3
Removed:
#superclass : 'MFTCard',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'rendering' }
10
Removed:
MFTPromisesCard >> renderContentOn: html [
11
Removed:
12
Removed:
self renderCardOn: html with: [
13
Removed:
html cardHeader: 'My promises to all users:'.
14
Removed:
html cardBody: [
15
Removed:
html listGroup: [
16
Removed:
html
17
Removed:
listGroupItem:
18
Removed:
'Every single feature shall always be free of charge.';
19
Removed:
listGroupItem:
20
Removed:
'No personal user data shall ever be sold, under any circumstances.';
21
Removed:
listGroupItem:
22
Removed:
'No external advertisements shall ever be displayed.' ].
23
Removed:
html form
24
Removed:
class: 'mt-3';
25
Removed:
with: [
26
Removed:
html outlineButton
27
Removed:
bePrimary;
28
Removed:
callback: [ self session pageManager setActivePageAt: #about ];
29
Removed:
with: 'Why did I create this web app?' ] ] ]
30
Removed:
]
src/MyFitnessTracker/MFTPublicLandingPage.class.st
@@ -1,17 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTPublicLandingPage',
3
Removed:
#superclass : 'MFTLandingPage',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'as yet unclassified' }
10
Removed:
MFTPublicLandingPage >> defaultCards [
11
Removed:
12
Removed:
^ {
13
Removed:
MFTMainPublicCard new.
14
Removed:
MFTWorkoutsCard new.
15
Removed:
MFTWeightsCard new.
16
Removed:
MFTPromisesCard new }
17
Removed:
]
src/MyFitnessTracker/MFTRegisterProspectEmailHeader.class.st
@@ -1,138 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTRegisterProspectEmailHeader',
3
Removed:
#superclass : 'MFTHeaderComponent',
4
Removed:
#instVars : [
5
Removed:
'email',
6
Removed:
'localPart',
7
Removed:
'domain',
8
Removed:
'isDismissed'
9
Removed:
],
10
Removed:
#category : 'MyFitnessTracker-Components',
11
Removed:
#package : 'MyFitnessTracker',
12
Removed:
#tag : 'Components'
13
Removed:
}
14
Removed:
15
Removed:
{ #category : 'adding' }
16
Removed:
MFTRegisterProspectEmailHeader >> addUserEmail [
17
Removed:
18
Removed:
| emailToCheck emailPattern |
19
Removed:
emailToCheck := self call: MFTEmailSignupDialog new.
20
Removed:
emailPattern := '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]{3,}\.[a-zA-Z]{2,}$'
21
Removed:
asRegex.
22
Removed:
(emailToCheck isNil or: [ (emailPattern matches: emailToCheck) not ])
23
Removed:
ifTrue: [ ^ nil ].
24
Removed:
email := emailToCheck.
25
Removed:
MFTSQLite3Database storeProspectEmail: email
26
Removed:
]
27
Removed:
28
Removed:
{ #category : 'initialization' }
29
Removed:
MFTRegisterProspectEmailHeader >> initialize [
30
Removed:
31
Removed:
super initialize.
32
Removed:
isDismissed := false
33
Removed:
]
34
Removed:
35
Removed:
{ #category : 'accessing' }
36
Removed:
MFTRegisterProspectEmailHeader >> isDismissed [
37
Removed:
38
Removed:
^ isDismissed
39
Removed:
]
40
Removed:
41
Removed:
{ #category : 'accessing' }
42
Removed:
MFTRegisterProspectEmailHeader >> isDismissed: anObject [
43
Removed:
44
Removed:
isDismissed := anObject
45
Removed:
]
46
Removed:
47
Removed:
{ #category : 'rendering' }
48
Removed:
MFTRegisterProspectEmailHeader >> renderContentOn: html [
49
Removed:
50
Removed:
isDismissed ifTrue: [ ^ self ].
51
Removed:
email
52
Removed:
ifNil: [
53
Removed:
html alert
54
Removed:
beWarning;
55
Removed:
marginBottom: 0;
56
Removed:
with: [
57
Removed:
html alertHeading: 'This app is still in beta.'.
58
Removed:
html paragraph: [
59
Removed:
html
60
Removed:
text:
61
Removed:
'My Fitness Tracker is still under heavy development, with improvements being made daily.';
62
Removed:
space;
63
Removed:
text: 'I can''t wait to share the finished product with you!' ].
64
Removed:
html form with: [
65
Removed:
html label
66
Removed:
style: 'margin-right: 1em';
67
Removed:
with: [ html strong: 'Want to know when version 1.0 is out?' ].
68
Removed:
html buttonGroup: [
69
Removed:
html formButton
70
Removed:
beInfo;
71
Removed:
callback: [ self addUserEmail ];
72
Removed:
with: 'E-mail me!'.
73
Removed:
html formButton
74
Removed:
beSecondary;
75
Removed:
callback: [ isDismissed := true ];
76
Removed:
with: 'I''m not interested' ] ] ] ]
77
Removed:
ifNotNil: [
78
Removed:
html alert
79
Removed:
beSuccess;
80
Removed:
beDismissible;
81
Removed:
marginBottom: 0;
82
Removed:
with: [
83
Removed:
html closeButton dataDismiss: 'alert'.
84
Removed:
html
85
Removed:
text: 'You''ll be notified at ';
86
Removed:
strong: email;
87
Removed:
text: ' when My Fitness Tracker is ready for you!' ] ]
88
Removed:
]
89
Removed:
90
Removed:
{ #category : 'rendering' }
91
Removed:
MFTRegisterProspectEmailHeader >> renderEmailInputModalButtonOn: html [
92
Removed:
93
Removed:
html formButton
94
Removed:
bePrimary;
95
Removed:
beSmall;
96
Removed:
dataToggle: 'modal';
97
Removed:
dataTarget: '#emailInputModal';
98
Removed:
with: 'E-mail me!'
99
Removed:
]
100
Removed:
101
Removed:
{ #category : 'rendering' }
102
Removed:
MFTRegisterProspectEmailHeader >> renderEmailInputModalOn: html [
103
Removed:
104
Removed:
html modal
105
Removed:
id: 'emailInputModal';
106
Removed:
fade;
107
Removed:
with: [
108
Removed:
html modalDialog
109
Removed:
beCentered;
110
Removed:
with: [
111
Removed:
html modalContent: [
112
Removed:
html form: [
113
Removed:
html modalHeader: [
114
Removed:
html modalTitle
115
Removed:
level5;
116
Removed:
with:
117
Removed:
'You''ll be the first to know when My Fitness Tracker is ready to use.'.
118
Removed:
html modalCloseButton ].
119
Removed:
120
Removed:
html modalBody: [
121
Removed:
html inputGroup: [
122
Removed:
html formTextInput
123
Removed:
callback: [ :value | localPart := value ];
124
Removed:
placeholder: 'my-user'.
125
Removed:
html inputGroupAppend: [ html inputGroupText: '@' ].
126
Removed:
html formTextInput
127
Removed:
callback: [ :value | domain := value ];
128
Removed:
placeholder: 'example.com' ] ].
129
Removed:
130
Removed:
html modalFooter: [
131
Removed:
html paragraph:
132
Removed:
'You will receive a single e-mail when the app is ready to use. Your e-mail address shall not be used in any other way.'.
133
Removed:
html formButton
134
Removed:
bePrimary;
135
Removed:
beSubmit;
136
Removed:
callback: [ email := localPart , '@' , domain ];
137
Removed:
with: 'Confirm' ] ] ] ] ]
138
Removed:
]
src/MyFitnessTracker/MFTRepsDistributionGraph.class.st
@@ -1,54 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTRepsDistributionGraph',
3
Removed:
#superclass : 'MFTGraph',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'as yet unclassified' }
10
Removed:
MFTRepsDistributionGraph >> crunchTheNumbers [
11
Removed:
"TODO: This looks ugly."
12
Removed:
13
Removed:
| sets repTally |
14
Removed:
sets := self session user workouts flatCollect: #sets.
15
Removed:
repTally := Dictionary new.
16
Removed:
sets do: [ :set |
17
Removed:
repTally at: set exercise englishName ifAbsentPut: [ 0 ].
18
Removed:
repTally
19
Removed:
at: set exercise englishName
20
Removed:
put: (repTally at: set exercise englishName) + set reps ].
21
Removed:
22
Removed:
labels := repTally keys.
23
Removed:
data := repTally values
24
Removed:
]
25
Removed:
26
Removed:
{ #category : 'initialization' }
27
Removed:
MFTRepsDistributionGraph >> initialize [
28
Removed:
29
Removed:
super initialize.
30
Removed:
self crunchTheNumbers
31
Removed:
]
32
Removed:
33
Removed:
{ #category : 'rendering' }
34
Removed:
MFTRepsDistributionGraph >> renderContentOn: html [
35
Removed:
36
Removed:
html canvas id: 'repsVsTimeChart'.
37
Removed:
html script:
38
Removed:
'var ctx = document.getElementById("repsVsTimeChart").getContext("2d");
39
Removed:
var workoutChart = new Chart(ctx, {
40
Removed:
type: "doughnut",
41
Removed:
data: {
42
Removed:
labels: ' , labels asJavascript , ',
43
Removed:
datasets: [{
44
Removed:
data: ' , data asJavascript , '
45
Removed:
}]
46
Removed:
},
47
Removed:
options: {
48
Removed:
title: {
49
Removed:
display: true,
50
Removed:
text: "Reps by exercise"
51
Removed:
}
52
Removed:
}
53
Removed:
});'
54
Removed:
]
src/MyFitnessTracker/MFTRootComponent.class.st
@@ -1,102 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTRootComponent',
3
Removed:
#superclass : 'SBSRootComponent',
4
Removed:
#instVars : [
5
Removed:
'main'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'testing' }
13
Removed:
MFTRootComponent class >> canBeRoot [
14
Removed:
15
Removed:
^ true
16
Removed:
]
17
Removed:
18
Removed:
{ #category : 'class initialization' }
19
Removed:
MFTRootComponent class >> initialize [
20
Removed:
21
Removed:
| application libraries |
22
Removed:
application := WAAdmin register: self asApplicationAt: 'mft'.
23
Removed:
application preferenceAt: #sessionClass put: MFTSession.
24
Removed:
libraries := MFTUtils isDevelopmentImage
25
Removed:
ifTrue: [ self libraries at: #development ]
26
Removed:
ifFalse: [ self libraries at: #deployment ].
27
Removed:
libraries do: [ :library | application addLibrary: library ].
28
Removed:
^ application
29
Removed:
]
30
Removed:
31
Removed:
{ #category : 'class initialization' }
32
Removed:
MFTRootComponent class >> libraries [
33
Removed:
34
Removed:
^ Dictionary new
35
Removed:
at: #development put: {
36
Removed:
JQDevelopmentLibrary.
37
Removed:
JQUiDevelopmentLibrary.
38
Removed:
SBSDevelopmentLibrary };
39
Removed:
at: #deployment put: {
40
Removed:
JQGoogleLibrary.
41
Removed:
JQUiGoogleLibrary.
42
Removed:
SBSCDNDeploymentLibrary };
43
Removed:
yourself
44
Removed:
]
45
Removed:
46
Removed:
{ #category : 'adding' }
47
Removed:
MFTRootComponent >> addBootstrapJSScriptTo: html [
48
Removed:
49
Removed:
| library |
50
Removed:
library := MFTUtils isDevelopmentImage
51
Removed:
ifTrue: [ SBSDeploymentLibrary ]
52
Removed:
ifFalse: [ SBSCDNDeploymentLibrary ].
53
Removed:
library addLoadScriptTo: html
54
Removed:
]
55
Removed:
56
Removed:
{ #category : 'rendering' }
57
Removed:
MFTRootComponent >> children [
58
Removed:
59
Removed:
^ { main }
60
Removed:
]
61
Removed:
62
Removed:
{ #category : 'initialization' }
63
Removed:
MFTRootComponent >> initialize [
64
Removed:
65
Removed:
super initialize.
66
Removed:
main := MFTScreenComponent new
67
Removed:
]
68
Removed:
69
Removed:
{ #category : 'initialization' }
70
Removed:
MFTRootComponent >> main: aComponent [
71
Removed:
72
Removed:
main := aComponent
73
Removed:
]
74
Removed:
75
Removed:
{ #category : 'rendering' }
76
Removed:
MFTRootComponent >> renderContentOn: html [
77
Removed:
78
Removed:
html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
79
Removed:
html render: main.
80
Removed:
self addBootstrapJSScriptTo: html
81
Removed:
]
82
Removed:
83
Removed:
{ #category : 'updating' }
84
Removed:
MFTRootComponent >> updateRoot: anHtmlRoot [
85
Removed:
86
Removed:
super updateRoot: anHtmlRoot.
87
Removed:
anHtmlRoot beHtml5.
88
Removed:
anHtmlRoot meta
89
Removed:
name: 'viewport';
90
Removed:
content: 'width=device-width, initial-scale=1'.
91
Removed:
anHtmlRoot title: 'My Fitness Tracker ' , self version.
92
Removed:
anHtmlRoot javascript url: 'https://cdn.jsdelivr.net/npm/chart.js'.
93
Removed:
anHtmlRoot link
94
Removed:
beShortcutIcon;
95
Removed:
url: MFTFileLibrary / #faviconPng
96
Removed:
]
97
Removed:
98
Removed:
{ #category : 'versions' }
99
Removed:
MFTRootComponent >> version [
100
Removed:
101
Removed:
^ MFTUtils version ifNil: [ '[DEVELOP]' ]
102
Removed:
]
src/MyFitnessTracker/MFTSandboxPage.class.st
@@ -1,22 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSandboxPage',
3
Removed:
#superclass : 'MFTPage',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'initialization' }
10
Removed:
MFTSandboxPage >> initialize [
11
Removed:
12
Removed:
super initialize.
13
Removed:
title := 'sandbox'
14
Removed:
]
15
Removed:
16
Removed:
{ #category : 'rendering' }
17
Removed:
MFTSandboxPage >> renderContentOn: html [
18
Removed:
19
Removed:
self renderPageOn: html with: [
20
Removed:
html text: 'Welcome to the sandbox page.'.
21
Removed:
MFTSQLite3Database allMuscleGroups do: [ :each | html render: each ] ]
22
Removed:
]
src/MyFitnessTracker/MFTScreenComponent.class.st
@@ -1,39 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTScreenComponent',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'pm'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'hooks' }
13
Removed:
MFTScreenComponent >> children [
14
Removed:
15
Removed:
^ {
16
Removed:
pm header.
17
Removed:
pm mainNavigationBar.
18
Removed:
pm activePage.
19
Removed:
pm footer }
20
Removed:
]
21
Removed:
22
Removed:
{ #category : 'initialization' }
23
Removed:
MFTScreenComponent >> initialize [
24
Removed:
25
Removed:
super initialize.
26
Removed:
pm := self session pageManager
27
Removed:
]
28
Removed:
29
Removed:
{ #category : 'rendering' }
30
Removed:
MFTScreenComponent >> renderContentOn: html [
31
Removed:
32
Removed:
(self session user isLoggedIn not and: [
33
Removed:
MFTUtils isDevelopmentImage not ]) ifTrue: [
34
Removed:
html render: pm header ].
35
Removed:
html
36
Removed:
render: pm mainNavigationBar;
37
Removed:
render: pm activePage;
38
Removed:
render: pm footer
39
Removed:
]
src/MyFitnessTracker/MFTSecondaryMuscleGroupsDialog.class.st
@@ -1,65 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSecondaryMuscleGroupsDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'secondaryMuscleGroups'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'initialization' }
13
Removed:
MFTSecondaryMuscleGroupsDialog >> initialize [
14
Removed:
15
Removed:
super initialize.
16
Removed:
secondaryMuscleGroups := OrderedCollection new
17
Removed:
]
18
Removed:
19
Removed:
{ #category : 'rendering' }
20
Removed:
MFTSecondaryMuscleGroupsDialog >> renderContentOn: html [
21
Removed:
22
Removed:
| allMuscleGroups selectedMuscles |
23
Removed:
allMuscleGroups := MFTSQLite3Database allMuscleGroups.
24
Removed:
"selectedMuscles := allMuscles
25
Removed:
detect: [ :each |
26
Removed:
each englishName
27
Removed:
= (secondaryMuscles collect: #englishName) ]
28
Removed:
ifNone: [ nil ]."
29
Removed:
30
Removed:
self renderDialogOn: html with: [
31
Removed:
html multiSelect formControl
32
Removed:
list: allMuscleGroups;
33
Removed:
"selected: selectedMuscles;"labels: [ :muscleGroup |
34
Removed:
muscleGroup englishName ];
35
Removed:
callback: [ :groups |
36
Removed:
groups ifNotEmpty: [ secondaryMuscleGroups := groups ] ] ]
37
Removed:
]
38
Removed:
39
Removed:
{ #category : 'rendering' }
40
Removed:
MFTSecondaryMuscleGroupsDialog >> renderSubmitOn: html [
41
Removed:
42
Removed:
html buttonGroup: [
43
Removed:
html formButton
44
Removed:
beSubmit;
45
Removed:
bePrimary;
46
Removed:
callback: [ self answer: secondaryMuscleGroups ];
47
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
48
Removed:
html formButton
49
Removed:
beSubmit;
50
Removed:
beSecondary;
51
Removed:
callback: [ self answer: OrderedCollection new ];
52
Removed:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
53
Removed:
]
54
Removed:
55
Removed:
{ #category : 'accessing' }
56
Removed:
MFTSecondaryMuscleGroupsDialog >> secondaryMuscleGroups [
57
Removed:
58
Removed:
^ secondaryMuscleGroups
59
Removed:
]
60
Removed:
61
Removed:
{ #category : 'accessing' }
62
Removed:
MFTSecondaryMuscleGroupsDialog >> secondaryMuscleGroups: anObject [
63
Removed:
64
Removed:
secondaryMuscleGroups := anObject
65
Removed:
]
src/MyFitnessTracker/MFTSecondaryMuscleGroupsFormComponent.class.st
@@ -1,40 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSecondaryMuscleGroupsFormComponent',
3
Removed:
#superclass : 'MFTFormComponent',
4
Removed:
#instVars : [
5
Removed:
'allMuscleGroups'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Forms',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Forms'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'as yet unclassified' }
13
Removed:
MFTSecondaryMuscleGroupsFormComponent >> getSelectedMuscleGroups [
14
Removed:
15
Removed:
| selectedMuscleGroups |
16
Removed:
selectedMuscleGroups := allMuscleGroups select: [ :muscleGroup |
17
Removed:
modelObject secondaryMuscleGroupIds
18
Removed:
includes: muscleGroup id ].
19
Removed:
^ selectedMuscleGroups
20
Removed:
]
21
Removed:
22
Removed:
{ #category : 'initialization' }
23
Removed:
MFTSecondaryMuscleGroupsFormComponent >> initialize [
24
Removed:
25
Removed:
super initialize.
26
Removed:
heading := 'Secondary Groups'.
27
Removed:
allMuscleGroups := MFTSQLite3Database allMuscleGroups
28
Removed:
]
29
Removed:
30
Removed:
{ #category : 'rendering' }
31
Removed:
MFTSecondaryMuscleGroupsFormComponent >> renderContentOn: html [
32
Removed:
33
Removed:
self renderFormComponentOn: html with: [
34
Removed:
html multiSelect formControl
35
Removed:
list: allMuscleGroups;
36
Removed:
selected: self getSelectedMuscleGroups;
37
Removed:
labels: [ :muscleGroup | muscleGroup englishName ];
38
Removed:
callback: [ :muscleGroups |
39
Removed:
modelObject secondaryMuscleGroups: muscleGroups ] ]
40
Removed:
]
src/MyFitnessTracker/MFTSetDialog.class.st
@@ -1,10 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSetDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'set'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
src/MyFitnessTracker/MFTSetGroupDialog.class.st
@@ -1,319 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSetGroupDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'allExercises',
6
Removed:
'selectedExercises',
7
Removed:
'setGroup'
8
Removed:
],
9
Removed:
#category : 'MyFitnessTracker-Dialogs',
10
Removed:
#package : 'MyFitnessTracker',
11
Removed:
#tag : 'Dialogs'
12
Removed:
}
13
Removed:
14
Removed:
{ #category : 'as yet unclassified' }
15
Removed:
MFTSetGroupDialog >> addSet [
16
Removed:
17
Removed:
| set |
18
Removed:
set := self call:
19
Removed:
(MFTSetDialog newForExercise: selectedExercises first).
20
Removed:
set ifNil: [ ^ self ].
21
Removed:
setGroup sets ifEmpty: [ setGroup := MFTSetGroup new ].
22
Removed:
setGroup addSet: (set exercise: selectedExercises first)
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'as yet unclassified' }
26
Removed:
MFTSetGroupDialog >> addSets: aMFTExerciseCollection [
27
Removed:
28
Removed:
| sets |
29
Removed:
sets := self call:
30
Removed:
((MFTSetsDialog newForExercises:
31
Removed:
aMFTExerciseCollection) subheading:
32
Removed:
((selectedExercises collect: #englishName) joinUsing: '/')).
33
Removed:
sets ifNotEmpty: [ setGroup addSets: sets ]
34
Removed:
]
35
Removed:
36
Removed:
{ #category : 'as yet unclassified' }
37
Removed:
MFTSetGroupDialog >> editSet: aMFTSet [
38
Removed:
39
Removed:
| oldSet newSet |
40
Removed:
oldSet := aMFTSet.
41
Removed:
newSet := self call:
42
Removed:
(MFTSetDialog
43
Removed:
edit: oldSet copy
44
Removed:
withExercise: selectedExercises first).
45
Removed:
newSet ifNotNil: [ setGroup updateSet: oldSet with: newSet ]
46
Removed:
]
47
Removed:
48
Removed:
{ #category : 'as yet unclassified' }
49
Removed:
MFTSetGroupDialog >> editSets: aMFTSetCollection [
50
Removed:
51
Removed:
| oldSets newSets |
52
Removed:
oldSets := aMFTSetCollection.
53
Removed:
newSets := self call:
54
Removed:
(MFTSetsDialog
55
Removed:
edit: oldSets copy
56
Removed:
withExercises: selectedExercises).
57
Removed:
newSets ifNotEmpty: [ setGroup updateSets: oldSets with: newSets ]
58
Removed:
]
59
Removed:
60
Removed:
{ #category : 'initialization' }
61
Removed:
MFTSetGroupDialog >> initialize [
62
Removed:
63
Removed:
super initialize.
64
Removed:
setGroup := MFTSetGroup new sets:
65
Removed:
(OrderedCollection with: (MFTSet new reps: 8)).
66
Removed:
allExercises := MFTSQLite3Database allExercises.
67
Removed:
selectedExercises := OrderedCollection with: allExercises first
68
Removed:
]
69
Removed:
70
Removed:
{ #category : 'as yet unclassified' }
71
Removed:
MFTSetGroupDialog >> makeGiantSet [
72
Removed:
73
Removed:
self heading: 'Giant Sets'.
74
Removed:
selectedExercises := OrderedCollection withAll:
75
Removed:
(allExercises copyFrom: 1 to: 5).
76
Removed:
self populateSetsForExercises: selectedExercises
77
Removed:
]
78
Removed:
79
Removed:
{ #category : 'as yet unclassified' }
80
Removed:
MFTSetGroupDialog >> makeRegularExercise [
81
Removed:
82
Removed:
self heading: 'Sets'.
83
Removed:
selectedExercises := OrderedCollection with: allExercises first.
84
Removed:
self populateSetsForExercises: selectedExercises
85
Removed:
]
86
Removed:
87
Removed:
{ #category : 'as yet unclassified' }
88
Removed:
MFTSetGroupDialog >> makeSuperset [
89
Removed:
90
Removed:
self heading: 'Supersets'.
91
Removed:
selectedExercises := OrderedCollection
92
Removed:
with: allExercises first
93
Removed:
with: allExercises second.
94
Removed:
self populateSetsForExercises: selectedExercises
95
Removed:
]
96
Removed:
97
Removed:
{ #category : 'as yet unclassified' }
98
Removed:
MFTSetGroupDialog >> makeTriset [
99
Removed:
100
Removed:
self heading: 'Tri-sets'.
101
Removed:
selectedExercises := OrderedCollection
102
Removed:
with: allExercises first
103
Removed:
with: allExercises second
104
Removed:
with: allExercises third.
105
Removed:
self populateSetsForExercises: selectedExercises
106
Removed:
]
107
Removed:
108
Removed:
{ #category : 'as yet unclassified' }
109
Removed:
MFTSetGroupDialog >> populateSetsForExercises: aMFTExerciseCollection [
110
Removed:
111
Removed:
setGroup := MFTSetGroup new sets:
112
Removed:
(aMFTExerciseCollection collect: [ :exercise |
113
Removed:
MFTSet new
114
Removed:
reps: 8;
115
Removed:
exercise: exercise ])
116
Removed:
]
117
Removed:
118
Removed:
{ #category : 'as yet unclassified' }
119
Removed:
MFTSetGroupDialog >> renderConfigureSetGroupButtonsOn: html [
120
Removed:
121
Removed:
selectedExercises size = 1
122
Removed:
ifTrue: [
123
Removed:
html outlineButton bePrimary beSmall
124
Removed:
class: 'me-2';
125
Removed:
callback: [ self addSet ];
126
Removed:
with: 'Add set' ]
127
Removed:
ifFalse: [
128
Removed:
html outlineButton bePrimary beSmall
129
Removed:
class: 'me-2';
130
Removed:
callback: [ self addSets: selectedExercises ];
131
Removed:
with: 'Add set group' ].
132
Removed:
html buttonGroup: [
133
Removed:
selectedExercises size ~~ 1 ifTrue: [
134
Removed:
html outlineButton beSecondary beSmall
135
Removed:
callback: [ self makeRegularExercise ];
136
Removed:
with: 'Regular exercise' ].
137
Removed:
selectedExercises size ~~ 2 ifTrue: [
138
Removed:
html outlineButton beSecondary beSmall
139
Removed:
callback: [ self makeSuperset ];
140
Removed:
with: 'Superset' ].
141
Removed:
selectedExercises size ~~ 3 ifTrue: [
142
Removed:
html outlineButton beSecondary beSmall
143
Removed:
callback: [ self makeTriset ];
144
Removed:
with: 'Tri-set' ].
145
Removed:
selectedExercises size < 4 ifTrue: [
146
Removed:
html outlineButton beSecondary beSmall
147
Removed:
callback: [ self makeGiantSet ];
148
Removed:
with: 'Giant set' ] ]
149
Removed:
]
150
Removed:
151
Removed:
{ #category : 'rendering' }
152
Removed:
MFTSetGroupDialog >> renderContentOn: html [
153
Removed:
154
Removed:
self renderDialogOn: html with: [
155
Removed:
self renderSelectExercisesOn: html.
156
Removed:
html horizontalRule.
157
Removed:
setGroup sets ifNotEmpty: [ self renderSetGroupTableOn: html ].
158
Removed:
self renderConfigureSetGroupButtonsOn: html ]
159
Removed:
]
160
Removed:
161
Removed:
{ #category : 'as yet unclassified' }
162
Removed:
MFTSetGroupDialog >> renderSelectExercisesOn: html [
163
Removed:
164
Removed:
| allSets |
165
Removed:
allSets := setGroup sets.
166
Removed:
selectedExercises doWithIndex: [ :selectedExercise :i |
167
Removed:
html formGroup: [
168
Removed:
html label: 'Exercise ' , (selectedExercises size > 1
169
Removed:
ifTrue: [ i asString ]
170
Removed:
ifFalse: [ '' ]).
171
Removed:
html select formControl
172
Removed:
list: allExercises;
173
Removed:
selected: selectedExercise;
174
Removed:
callback: [ :exercise |
175
Removed:
selectedExercises at: i put: exercise.
176
Removed:
self updateAllSelectedExercises ];
177
Removed:
labels: [ :each | each englishName ] ] ]
178
Removed:
]
179
Removed:
180
Removed:
{ #category : 'rendering' }
181
Removed:
MFTSetGroupDialog >> renderSetGroupRowsOn: html [
182
Removed:
183
Removed:
setGroup sets doWithIndex: [ :set :i |
184
Removed:
(i - 1 isDivisibleBy: selectedExercises size) ifTrue: [
185
Removed:
| selectedSets |
186
Removed:
selectedSets := setGroup sets atAll:
187
Removed:
(i to: i + selectedExercises size - 1).
188
Removed:
html tableRow with: [
189
Removed:
html tableData
190
Removed:
attributeAt: 'rowspan' put: selectedExercises size + 1;
191
Removed:
style: 'vertical-align: middle';
192
Removed:
with: [
193
Removed:
html buttonGroup: [
194
Removed:
html outlineButton
195
Removed:
beDanger;
196
Removed:
beSmall;
197
Removed:
callback: [ setGroup removeSets: selectedSets ];
198
Removed:
with: 'Remove'.
199
Removed:
html outlineButton
200
Removed:
beSecondary;
201
Removed:
beSmall;
202
Removed:
callback: [
203
Removed:
selectedSets size = 1
204
Removed:
ifTrue: [ self editSet: selectedSets first ]
205
Removed:
ifFalse: [ self editSets: selectedSets ] ];
206
Removed:
with: 'Edit'.
207
Removed:
selectedExercises size = 1 ifTrue: [
208
Removed:
html outlineButton
209
Removed:
beSecondary;
210
Removed:
beSmall;
211
Removed:
callback: [ setGroup duplicateSet: selectedSets first ];
212
Removed:
with: 'Duplicate' ] ] ] ] ].
213
Removed:
214
Removed:
html tableRow: [ "This row spans the amount of sets for a given exercise."
215
Removed:
setGroup isIrregularSetGroup ifTrue: [
216
Removed:
html tableData with: set exercise englishName capitalized ].
217
Removed:
html tableData
218
Removed:
style: 'text-align: right';
219
Removed:
with: set reps.
220
Removed:
setGroup hasLoad ifTrue: [
221
Removed:
html tableData
222
Removed:
style: 'text-align: right';
223
Removed:
with: set load ].
224
Removed:
setGroup hasRpe ifTrue: [
225
Removed:
html tableData
226
Removed:
style: 'text-align: right';
227
Removed:
with: set rpe ].
228
Removed:
setGroup hasRestAfter ifTrue: [
229
Removed:
html tableData
230
Removed:
style: 'text-align: right';
231
Removed:
with: set restAfter ] ] ]
232
Removed:
]
233
Removed:
234
Removed:
{ #category : 'rendering' }
235
Removed:
MFTSetGroupDialog >> renderSetGroupTableHeaderOn: html [
236
Removed:
237
Removed:
html tableHead: [
238
Removed:
html tableHeading.
239
Removed:
setGroup isIrregularSetGroup ifTrue: [
240
Removed:
html tableHeading with: 'Exercise' ].
241
Removed:
html tableHeading
242
Removed:
style: 'text-align: right';
243
Removed:
with: 'Reps'.
244
Removed:
setGroup hasLoad ifTrue: [
245
Removed:
html tableHeading
246
Removed:
style: 'text-align: right';
247
Removed:
with: 'Load' ].
248
Removed:
setGroup hasRpe ifTrue: [
249
Removed:
html tableHeading
250
Removed:
style: 'text-align: right';
251
Removed:
with: 'RPE' ].
252
Removed:
setGroup hasRestAfter ifTrue: [
253
Removed:
html tableHeading
254
Removed:
style: 'text-align: right';
255
Removed:
with: 'Rest After (s)' ] ]
256
Removed:
]
257
Removed:
258
Removed:
{ #category : 'rendering' }
259
Removed:
MFTSetGroupDialog >> renderSetGroupTableOn: html [
260
Removed:
261
Removed:
html table
262
Removed:
class: 'table table-bordered';
263
Removed:
with: [
264
Removed:
self renderSetGroupTableHeaderOn: html.
265
Removed:
self renderSetGroupRowsOn: html ]
266
Removed:
]
267
Removed:
268
Removed:
{ #category : 'as yet unclassified' }
269
Removed:
MFTSetGroupDialog >> renderSubmitOn: html [
270
Removed:
271
Removed:
html buttonGroup: [
272
Removed:
html formButton
273
Removed:
beSubmit;
274
Removed:
bePrimary;
275
Removed:
callback: [ self answer: setGroup ];
276
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
277
Removed:
html formButton
278
Removed:
beSubmit;
279
Removed:
beSecondary;
280
Removed:
callback: [ self answer: nil ];
281
Removed:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
282
Removed:
]
283
Removed:
284
Removed:
{ #category : 'as yet unclassified' }
285
Removed:
MFTSetGroupDialog >> selectedExercises [
286
Removed:
287
Removed:
^ selectedExercises
288
Removed:
]
289
Removed:
290
Removed:
{ #category : 'as yet unclassified' }
291
Removed:
MFTSetGroupDialog >> selectedExercises: aMFTExerciseCollection [
292
Removed:
293
Removed:
selectedExercises := aMFTExerciseCollection
294
Removed:
]
295
Removed:
296
Removed:
{ #category : 'as yet unclassified' }
297
Removed:
MFTSetGroupDialog >> setGroup [
298
Removed:
299
Removed:
^ setGroup
300
Removed:
]
301
Removed:
302
Removed:
{ #category : 'as yet unclassified' }
303
Removed:
MFTSetGroupDialog >> setGroup: anObject [
304
Removed:
305
Removed:
setGroup := anObject
306
Removed:
]
307
Removed:
308
Removed:
{ #category : 'as yet unclassified' }
309
Removed:
MFTSetGroupDialog >> updateAllSelectedExercises [
310
Removed:
311
Removed:
| allSets |
312
Removed:
allSets := setGroup sets.
313
Removed:
selectedExercises doWithIndex: [ :selectedExercise :i |
314
Removed:
i to: allSets size by: selectedExercises size do: [ :j |
315
Removed:
| set |
316
Removed:
"Update every nth set to reflect the newly selected exercise selectedExercises at: i."
317
Removed:
set := allSets at: j.
318
Removed:
set exercise: selectedExercise ] ]
319
Removed:
]
src/MyFitnessTracker/MFTSetGroupForm.class.st
@@ -1,144 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSetGroupForm',
3
Removed:
#superclass : 'MFTForm',
4
Removed:
#instVars : [
5
Removed:
'exerciseCount',
6
Removed:
'renderRpe',
7
Removed:
'renderRestAfter'
8
Removed:
],
9
Removed:
#category : 'MyFitnessTracker-Forms',
10
Removed:
#package : 'MyFitnessTracker',
11
Removed:
#tag : 'Forms'
12
Removed:
}
13
Removed:
14
Removed:
{ #category : 'as yet unclassified' }
15
Removed:
MFTSetGroupForm class >> forExercises: anMFTExerciseCollection [
16
Removed:
17
Removed:
| setGroup |
18
Removed:
setGroup := MFTSetGroup new addSets:
19
Removed:
(anMFTExerciseCollection collect: [ :exercise |
20
Removed:
MFTSet new
21
Removed:
exercise: exercise;
22
Removed:
reps: 8 ]).
23
Removed:
^ self new
24
Removed:
modelObject: setGroup;
25
Removed:
exerciseCount: anMFTExerciseCollection size;
26
Removed:
yourself
27
Removed:
]
28
Removed:
29
Removed:
{ #category : 'accessing' }
30
Removed:
MFTSetGroupForm >> exerciseCount: anInteger [
31
Removed:
32
Removed:
exerciseCount := anInteger
33
Removed:
]
34
Removed:
35
Removed:
{ #category : 'initialization' }
36
Removed:
MFTSetGroupForm >> initialize [
37
Removed:
38
Removed:
super initialize.
39
Removed:
modelObject := MFTSetGroup new addSet: (MFTSet new reps: 8).
40
Removed:
heading := 'Sets'.
41
Removed:
renderRpe := false.
42
Removed:
renderRestAfter := false
43
Removed:
]
44
Removed:
45
Removed:
{ #category : 'rendering' }
46
Removed:
MFTSetGroupForm >> renderContentOn: html [
47
Removed:
48
Removed:
self renderFormOn: html with: [
49
Removed:
self renderSetsButtonGroupOn: html.
50
Removed:
self renderSetGroupTableOn: html ]
51
Removed:
]
52
Removed:
53
Removed:
{ #category : 'rendering' }
54
Removed:
MFTSetGroupForm >> renderSetGroupRowsOn: html [
55
Removed:
56
Removed:
| selectedExercises renderSetGroupExercisesName |
57
Removed:
selectedExercises := modelObject sets collect: #exercise.
58
Removed:
renderSetGroupExercisesName := modelObject isIrregularSetGroup.
59
Removed:
60
Removed:
modelObject sets do: [ :set |
61
Removed:
html tableRow: [
62
Removed:
renderSetGroupExercisesName ifTrue: [
63
Removed:
html tableData with: set exercise englishName capitalized ].
64
Removed:
html tableData: [
65
Removed:
html numberInput formControl
66
Removed:
value: set reps;
67
Removed:
callback: [ :value | set reps: value asInteger ] ].
68
Removed:
html tableData: [
69
Removed:
html numberInput formControl
70
Removed:
value: set load;
71
Removed:
callback: [ :value |
72
Removed:
set load:
73
Removed:
(value ifNotEmpty: [ value asNumber ] ifEmpty: [ nil ]) ] ].
74
Removed:
renderRpe ifTrue: [
75
Removed:
html tableData: [
76
Removed:
html numberInput formControl
77
Removed:
value: set rpe;
78
Removed:
callback: [ :value |
79
Removed:
set rpe:
80
Removed:
(value ifNotEmpty: [ value asInteger ] ifEmpty: [ nil ]) ] ] ].
81
Removed:
renderRestAfter ifTrue: [
82
Removed:
html tableData: [
83
Removed:
html numberInput formControl
84
Removed:
value: set restAfter;
85
Removed:
callback: [ :value |
86
Removed:
set restAfter:
87
Removed:
(value ifNotEmpty: [ value asNumber ] ifEmpty: [ nil ]) ] ] ] ] ]
88
Removed:
]
89
Removed:
90
Removed:
{ #category : 'rendering' }
91
Removed:
MFTSetGroupForm >> renderSetGroupTableHeaderOn: html [
92
Removed:
93
Removed:
html tableHead: [
94
Removed:
modelObject isIrregularSetGroup ifTrue: [
95
Removed:
html tableHeading with: 'Exercise' ].
96
Removed:
html tableHeading with: 'Reps'.
97
Removed:
html tableHeading with: 'Load'.
98
Removed:
renderRpe ifTrue: [ html tableHeading with: 'RPE' ].
99
Removed:
renderRestAfter ifTrue: [ html tableHeading with: 'Rest After (s)' ] ]
100
Removed:
]
101
Removed:
102
Removed:
{ #category : 'rendering' }
103
Removed:
MFTSetGroupForm >> renderSetGroupTableOn: html [
104
Removed:
105
Removed:
html table
106
Removed:
class: 'table table-bordered';
107
Removed:
with: [
108
Removed:
self renderSetGroupTableHeaderOn: html.
109
Removed:
self renderSetGroupRowsOn: html ]
110
Removed:
]
111
Removed:
112
Removed:
{ #category : 'rendering' }
113
Removed:
MFTSetGroupForm >> renderSetsButtonGroupOn: html [
114
Removed:
115
Removed:
html buttonGroup
116
Removed:
class: 'my-3';
117
Removed:
with: [
118
Removed:
html outlineButton
119
Removed:
bePrimary;
120
Removed:
beSmall;
121
Removed:
callback: [ modelObject addSets: modelObject sets ];
122
Removed:
with: 'Add sets'.
123
Removed:
renderRpe ifFalse: [
124
Removed:
html outlineButton
125
Removed:
beSecondary;
126
Removed:
beSmall;
127
Removed:
callback: [ renderRpe := true ];
128
Removed:
with: 'Add RPE' ].
129
Removed:
renderRestAfter ifFalse: [
130
Removed:
html outlineButton
131
Removed:
beSecondary;
132
Removed:
beSmall;
133
Removed:
callback: [ renderRestAfter := true ];
134
Removed:
with: 'Add rest' ].
135
Removed:
html outlineButton
136
Removed:
beDanger;
137
Removed:
beSmall;
138
Removed:
callback: [
139
Removed:
| firstSets |
140
Removed:
firstSets := modelObject sets first: exerciseCount.
141
Removed:
modelObject := MFTSetGroup new addSets: firstSets.
142
Removed:
renderRpe := renderRestAfter := false ];
143
Removed:
with: 'Reset' ]
144
Removed:
]
src/MyFitnessTracker/MFTSetGroupTask.class.st
@@ -1,63 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSetGroupTask',
3
Removed:
#superclass : 'WATask',
4
Removed:
#instVars : [
5
Removed:
'exercises',
6
Removed:
'exerciseCount',
7
Removed:
'setGroup'
8
Removed:
],
9
Removed:
#category : 'MyFitnessTracker-Tasks',
10
Removed:
#package : 'MyFitnessTracker',
11
Removed:
#tag : 'Tasks'
12
Removed:
}
13
Removed:
14
Removed:
{ #category : 'as yet unclassified' }
15
Removed:
MFTSetGroupTask class >> forExerciseCount: anInteger [
16
Removed:
17
Removed:
^ self new
18
Removed:
exerciseCount: anInteger;
19
Removed:
yourself
20
Removed:
]
21
Removed:
22
Removed:
{ #category : 'accessing' }
23
Removed:
MFTSetGroupTask >> exerciseCount: anObject [
24
Removed:
25
Removed:
exerciseCount := anObject
26
Removed:
]
27
Removed:
28
Removed:
{ #category : 'accessing' }
29
Removed:
MFTSetGroupTask >> exercises [
30
Removed:
31
Removed:
^ exercises
32
Removed:
]
33
Removed:
34
Removed:
{ #category : 'accessing' }
35
Removed:
MFTSetGroupTask >> exercises: anObject [
36
Removed:
37
Removed:
exercises := anObject
38
Removed:
]
39
Removed:
40
Removed:
{ #category : 'running' }
41
Removed:
MFTSetGroupTask >> go [
42
Removed:
43
Removed:
exercises := self call:
44
Removed:
(MFTExercisesForm forExerciseCount: exerciseCount).
45
Removed:
exercises ifNil: [ self answer: nil ].
46
Removed:
47
Removed:
setGroup := self call: (MFTSetGroupForm forExercises: exercises).
48
Removed:
setGroup ifNil: [ self answer: nil ].
49
Removed:
50
Removed:
self answer: setGroup
51
Removed:
]
52
Removed:
53
Removed:
{ #category : 'accessing' }
54
Removed:
MFTSetGroupTask >> setGroups [
55
Removed:
56
Removed:
^ setGroups
57
Removed:
]
58
Removed:
59
Removed:
{ #category : 'accessing' }
60
Removed:
MFTSetGroupTask >> setGroups: anObject [
61
Removed:
62
Removed:
setGroups := anObject
63
Removed:
]
src/MyFitnessTracker/MFTSetGroupsDialog.class.st
@@ -1,179 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSetGroupsDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'setGroups'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'as yet unclassified' }
13
Removed:
MFTSetGroupsDialog >> addExercise [
14
Removed:
15
Removed:
| setGroup |
16
Removed:
setGroup := self call: (MFTSetGroupDialog new heading: 'Exercise').
17
Removed:
setGroup ifNotNil: [ setGroups add: setGroup ]
18
Removed:
]
19
Removed:
20
Removed:
{ #category : 'as yet unclassified' }
21
Removed:
MFTSetGroupsDialog >> editSetGroup: aMFTSetGroup [
22
Removed:
23
Removed:
| oldSetGroup newSetGroup |
24
Removed:
oldSetGroup := aMFTSetGroup.
25
Removed:
newSetGroup := self call: (MFTSetGroupDialog edit: aMFTSetGroup copy).
26
Removed:
newSetGroup ifNotNil: [
27
Removed:
setGroups at: (setGroups indexOf: oldSetGroup) put: newSetGroup ]
28
Removed:
]
29
Removed:
30
Removed:
{ #category : 'as yet unclassified' }
31
Removed:
MFTSetGroupsDialog >> initialize [
32
Removed:
33
Removed:
super initialize.
34
Removed:
setGroups := OrderedCollection new
35
Removed:
]
36
Removed:
37
Removed:
{ #category : 'as yet unclassified' }
38
Removed:
MFTSetGroupsDialog >> renderAddSetGroupButtonsOn: html [
39
Removed:
40
Removed:
html outlineButton
41
Removed:
bePrimary;
42
Removed:
beSmall;
43
Removed:
callback: [ self addExercise ];
44
Removed:
with: 'Add exercise'
45
Removed:
]
46
Removed:
47
Removed:
{ #category : 'as yet unclassified' }
48
Removed:
MFTSetGroupsDialog >> renderContentOn: html [
49
Removed:
50
Removed:
self renderDialogOn: html with: [
51
Removed:
setGroups ifNotEmpty: [ self renderSetsTableOn: html ].
52
Removed:
self renderAddSetGroupButtonsOn: html ]
53
Removed:
]
54
Removed:
55
Removed:
{ #category : 'as yet unclassified' }
56
Removed:
MFTSetGroupsDialog >> renderLoad [
57
Removed:
58
Removed:
^ (setGroups flatCollect: #sets) anySatisfy: #hasLoad
59
Removed:
]
60
Removed:
61
Removed:
{ #category : 'as yet unclassified' }
62
Removed:
MFTSetGroupsDialog >> renderRestAfter [
63
Removed:
64
Removed:
^ (setGroups flatCollect: #sets) anySatisfy: #hasRestAfter
65
Removed:
]
66
Removed:
67
Removed:
{ #category : 'as yet unclassified' }
68
Removed:
MFTSetGroupsDialog >> renderRpe [
69
Removed:
70
Removed:
^ (setGroups flatCollect: #sets) anySatisfy: #hasRpe
71
Removed:
]
72
Removed:
73
Removed:
{ #category : 'as yet unclassified' }
74
Removed:
MFTSetGroupsDialog >> renderSetGroupButtonsOn: html For: setGroup [
75
Removed:
76
Removed:
html buttonGroup: [
77
Removed:
html outlineButton
78
Removed:
beDanger;
79
Removed:
beSmall;
80
Removed:
callback: [ setGroups remove: setGroup ];
81
Removed:
with: 'Remove'.
82
Removed:
html outlineButton
83
Removed:
beSecondary;
84
Removed:
beSmall;
85
Removed:
callback: [ self editSetGroup: setGroup ];
86
Removed:
with: 'Edit'.
87
Removed:
html outlineButton
88
Removed:
beSecondary;
89
Removed:
beSmall;
90
Removed:
callback: [
91
Removed:
setGroups add: setGroup afterIndex: (setGroups indexOf: setGroup) ];
92
Removed:
with: 'Duplicate' ]
93
Removed:
]
94
Removed:
95
Removed:
{ #category : 'as yet unclassified' }
96
Removed:
MFTSetGroupsDialog >> renderSetGroupsRowsOn: html [
97
Removed:
98
Removed:
| renderLoad renderRpe renderRestAfter |
99
Removed:
renderLoad := self renderLoad.
100
Removed:
renderRpe := self renderRpe.
101
Removed:
renderRestAfter := self renderRestAfter.
102
Removed:
setGroups do: [ :setGroup |
103
Removed:
html tableRow with: [
104
Removed:
html tableData
105
Removed:
attributeAt: 'rowspan' put: setGroup sets size + 1;
106
Removed:
style: 'vertical-align: middle';
107
Removed:
with: [ self renderSetGroupButtonsOn: html For: setGroup ] ].
108
Removed:
setGroup sets do: [ :set |
109
Removed:
html tableRow: [
110
Removed:
html tableData: set exercise englishName.
111
Removed:
html tableData: set reps.
112
Removed:
setGroup hasLoad ifTrue: [ html tableData: set load ].
113
Removed:
setGroup hasRpe ifTrue: [ html tableData: set rpe ].
114
Removed:
setGroup hasRestAfter ifTrue: [ html tableData: set restAfter ] ] ] ]
115
Removed:
]
116
Removed:
117
Removed:
{ #category : 'as yet unclassified' }
118
Removed:
MFTSetGroupsDialog >> renderSetGroupsTableHeaderOn: html [
119
Removed:
120
Removed:
html tableHead: [
121
Removed:
html
122
Removed:
tableHeading;
123
Removed:
tableHeading: 'Exercise';
124
Removed:
tableHeading: 'Reps'.
125
Removed:
self renderLoad ifTrue: [ html tableHeading: 'Load' ].
126
Removed:
self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
127
Removed:
self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
128
Removed:
]
129
Removed:
130
Removed:
{ #category : 'as yet unclassified' }
131
Removed:
MFTSetGroupsDialog >> renderSetsTableHeaderOn: html [
132
Removed:
133
Removed:
html tableHead: [
134
Removed:
html
135
Removed:
tableHeading;
136
Removed:
tableHeading: 'Exercise';
137
Removed:
tableHeading: 'Reps'.
138
Removed:
self renderLoad ifTrue: [ html tableHeading: 'Load' ].
139
Removed:
self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
140
Removed:
self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
141
Removed:
]
142
Removed:
143
Removed:
{ #category : 'as yet unclassified' }
144
Removed:
MFTSetGroupsDialog >> renderSetsTableOn: html [
145
Removed:
146
Removed:
html table
147
Removed:
class: 'table table-bordered';
148
Removed:
with: [
149
Removed:
self renderSetGroupsTableHeaderOn: html.
150
Removed:
self renderSetGroupsRowsOn: html ]
151
Removed:
]
152
Removed:
153
Removed:
{ #category : 'as yet unclassified' }
154
Removed:
MFTSetGroupsDialog >> renderSubmitOn: html [
155
Removed:
156
Removed:
html buttonGroup: [
157
Removed:
html formButton
158
Removed:
beSubmit;
159
Removed:
bePrimary;
160
Removed:
callback: [ self answer: setGroups ];
161
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
162
Removed:
html formButton
163
Removed:
beSubmit;
164
Removed:
beSecondary;
165
Removed:
callback: [ self answer: OrderedCollection new ];
166
Removed:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
167
Removed:
]
168
Removed:
169
Removed:
{ #category : 'as yet unclassified' }
170
Removed:
MFTSetGroupsDialog >> setGroups [
171
Removed:
172
Removed:
^ setGroups
173
Removed:
]
174
Removed:
175
Removed:
{ #category : 'as yet unclassified' }
176
Removed:
MFTSetGroupsDialog >> setGroups: anObject [
177
Removed:
178
Removed:
setGroups := anObject
179
Removed:
]
src/MyFitnessTracker/MFTSetGroupsDialogOld.class.st
@@ -1,179 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSetGroupsDialogOld',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'setGroups'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'adding' }
13
Removed:
MFTSetGroupsDialogOld >> addExercise [
14
Removed:
15
Removed:
| setGroup |
16
Removed:
setGroup := self call: (MFTSetGroupDialog new heading: 'Exercise').
17
Removed:
setGroup ifNotNil: [ setGroups add: setGroup ]
18
Removed:
]
19
Removed:
20
Removed:
{ #category : 'rendering' }
21
Removed:
MFTSetGroupsDialogOld >> editSetGroup: aMFTSetGroup [
22
Removed:
23
Removed:
| oldSetGroup newSetGroup |
24
Removed:
oldSetGroup := aMFTSetGroup.
25
Removed:
newSetGroup := self call: (MFTSetGroupDialog edit: aMFTSetGroup copy).
26
Removed:
newSetGroup ifNotNil: [
27
Removed:
setGroups at: (setGroups indexOf: oldSetGroup) put: newSetGroup ]
28
Removed:
]
29
Removed:
30
Removed:
{ #category : 'initialization' }
31
Removed:
MFTSetGroupsDialogOld >> initialize [
32
Removed:
33
Removed:
super initialize.
34
Removed:
setGroups := OrderedCollection new
35
Removed:
]
36
Removed:
37
Removed:
{ #category : 'rendering' }
38
Removed:
MFTSetGroupsDialogOld >> renderAddSetGroupButtonsOn: html [
39
Removed:
40
Removed:
html outlineButton
41
Removed:
bePrimary;
42
Removed:
beSmall;
43
Removed:
callback: [ self addExercise ];
44
Removed:
with: 'Add exercise'
45
Removed:
]
46
Removed:
47
Removed:
{ #category : 'rendering' }
48
Removed:
MFTSetGroupsDialogOld >> renderContentOn: html [
49
Removed:
50
Removed:
self renderDialogOn: html with: [
51
Removed:
setGroups ifNotEmpty: [ self renderSetsTableOn: html ].
52
Removed:
self renderAddSetGroupButtonsOn: html ]
53
Removed:
]
54
Removed:
55
Removed:
{ #category : 'as yet unclassified' }
56
Removed:
MFTSetGroupsDialogOld >> renderLoad [
57
Removed:
58
Removed:
^ (setGroups flatCollect: #sets) anySatisfy: #hasLoad
59
Removed:
]
60
Removed:
61
Removed:
{ #category : 'as yet unclassified' }
62
Removed:
MFTSetGroupsDialogOld >> renderRestAfter [
63
Removed:
64
Removed:
^ (setGroups flatCollect: #sets) anySatisfy: #hasRestAfter
65
Removed:
]
66
Removed:
67
Removed:
{ #category : 'as yet unclassified' }
68
Removed:
MFTSetGroupsDialogOld >> renderRpe [
69
Removed:
70
Removed:
^ (setGroups flatCollect: #sets) anySatisfy: #hasRpe
71
Removed:
]
72
Removed:
73
Removed:
{ #category : 'as yet unclassified' }
74
Removed:
MFTSetGroupsDialogOld >> renderSetGroupButtonsOn: html For: setGroup [
75
Removed:
76
Removed:
html buttonGroup: [
77
Removed:
html outlineButton
78
Removed:
beDanger;
79
Removed:
beSmall;
80
Removed:
callback: [ setGroups remove: setGroup ];
81
Removed:
with: 'Remove'.
82
Removed:
html outlineButton
83
Removed:
beSecondary;
84
Removed:
beSmall;
85
Removed:
callback: [ self editSetGroup: setGroup ];
86
Removed:
with: 'Edit'.
87
Removed:
html outlineButton
88
Removed:
beSecondary;
89
Removed:
beSmall;
90
Removed:
callback: [
91
Removed:
setGroups add: setGroup afterIndex: (setGroups indexOf: setGroup) ];
92
Removed:
with: 'Duplicate' ]
93
Removed:
]
94
Removed:
95
Removed:
{ #category : 'as yet unclassified' }
96
Removed:
MFTSetGroupsDialogOld >> renderSetGroupsRowsOn: html [
97
Removed:
98
Removed:
| renderLoad renderRpe renderRestAfter |
99
Removed:
renderLoad := self renderLoad.
100
Removed:
renderRpe := self renderRpe.
101
Removed:
renderRestAfter := self renderRestAfter.
102
Removed:
setGroups do: [ :setGroup |
103
Removed:
html tableRow with: [
104
Removed:
html tableData
105
Removed:
attributeAt: 'rowspan' put: setGroup sets size + 1;
106
Removed:
style: 'vertical-align: middle';
107
Removed:
with: [ self renderSetGroupButtonsOn: html For: setGroup ] ].
108
Removed:
setGroup sets do: [ :set |
109
Removed:
html tableRow: [
110
Removed:
html tableData: set exercise englishName.
111
Removed:
html tableData: set reps.
112
Removed:
setGroup hasLoad ifTrue: [ html tableData: set load ].
113
Removed:
setGroup hasRpe ifTrue: [ html tableData: set rpe ].
114
Removed:
setGroup hasRestAfter ifTrue: [ html tableData: set restAfter ] ] ] ]
115
Removed:
]
116
Removed:
117
Removed:
{ #category : 'as yet unclassified' }
118
Removed:
MFTSetGroupsDialogOld >> renderSetGroupsTableHeaderOn: html [
119
Removed:
120
Removed:
html tableHead: [
121
Removed:
html
122
Removed:
tableHeading;
123
Removed:
tableHeading: 'Exercise';
124
Removed:
tableHeading: 'Reps'.
125
Removed:
self renderLoad ifTrue: [ html tableHeading: 'Load' ].
126
Removed:
self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
127
Removed:
self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
128
Removed:
]
129
Removed:
130
Removed:
{ #category : 'as yet unclassified' }
131
Removed:
MFTSetGroupsDialogOld >> renderSetsTableHeaderOn: html [
132
Removed:
133
Removed:
html tableHead: [
134
Removed:
html
135
Removed:
tableHeading;
136
Removed:
tableHeading: 'Exercise';
137
Removed:
tableHeading: 'Reps'.
138
Removed:
self renderLoad ifTrue: [ html tableHeading: 'Load' ].
139
Removed:
self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
140
Removed:
self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
141
Removed:
]
142
Removed:
143
Removed:
{ #category : 'as yet unclassified' }
144
Removed:
MFTSetGroupsDialogOld >> renderSetsTableOn: html [
145
Removed:
146
Removed:
html table
147
Removed:
class: 'table table-bordered';
148
Removed:
with: [
149
Removed:
self renderSetGroupsTableHeaderOn: html.
150
Removed:
self renderSetGroupsRowsOn: html ]
151
Removed:
]
152
Removed:
153
Removed:
{ #category : 'rendering' }
154
Removed:
MFTSetGroupsDialogOld >> renderSubmitOn: html [
155
Removed:
156
Removed:
html buttonGroup: [
157
Removed:
html formButton
158
Removed:
beSubmit;
159
Removed:
bePrimary;
160
Removed:
callback: [ self answer: setGroups ];
161
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
162
Removed:
html formButton
163
Removed:
beSubmit;
164
Removed:
beSecondary;
165
Removed:
callback: [ self answer: OrderedCollection new ];
166
Removed:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
167
Removed:
]
168
Removed:
169
Removed:
{ #category : 'accessing' }
170
Removed:
MFTSetGroupsDialogOld >> setGroups [
171
Removed:
172
Removed:
^ setGroups
173
Removed:
]
174
Removed:
175
Removed:
{ #category : 'accessing' }
176
Removed:
MFTSetGroupsDialogOld >> setGroups: anObject [
177
Removed:
178
Removed:
setGroups := anObject
179
Removed:
]
src/MyFitnessTracker/MFTSetGroupsFormComponent.class.st
@@ -1,62 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSetGroupsFormComponent',
3
Removed:
#superclass : 'MFTFormComponent',
4
Removed:
#category : 'MyFitnessTracker-Forms',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Forms'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'adding' }
10
Removed:
MFTSetGroupsFormComponent >> addSetGroupsForExerciseCount: anInteger [
11
Removed:
12
Removed:
| setGroup |
13
Removed:
setGroup := self session pageManager activePage call:
14
Removed:
(MFTSetGroupTask forExerciseCount: anInteger).
15
Removed:
setGroup ifNotNil: [ modelObject addSetGroup: setGroup ]
16
Removed:
]
17
Removed:
18
Removed:
{ #category : 'initialization' }
19
Removed:
MFTSetGroupsFormComponent >> initialize [
20
Removed:
21
Removed:
super initialize.
22
Removed:
heading := 'Exercises'
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'rendering' }
26
Removed:
MFTSetGroupsFormComponent >> renderContentOn: html [
27
Removed:
28
Removed:
self renderFormComponentOn: html with: [
29
Removed:
modelObject renderSetGroupsTableOn: html.
30
Removed:
self renderSetGroupButtonsOn: html ]
31
Removed:
]
32
Removed:
33
Removed:
{ #category : 'rendering' }
34
Removed:
MFTSetGroupsFormComponent >> renderSetGroupButtonsOn: html [
35
Removed:
36
Removed:
html buttonGroup: [
37
Removed:
html outlineButton
38
Removed:
bePrimary;
39
Removed:
beSmall;
40
Removed:
callback: [ self addSetGroupsForExerciseCount: 1 ];
41
Removed:
with: 'Add exercise'.
42
Removed:
html outlineButton
43
Removed:
beSecondary;
44
Removed:
beSmall;
45
Removed:
callback: [ self addSetGroupsForExerciseCount: 2 ];
46
Removed:
with: 'Add superset'.
47
Removed:
html outlineButton
48
Removed:
beSecondary;
49
Removed:
beSmall;
50
Removed:
callback: [ self addSetGroupsForExerciseCount: 3 ];
51
Removed:
with: 'Add tri-set'.
52
Removed:
html outlineButton
53
Removed:
beSecondary;
54
Removed:
beSmall;
55
Removed:
callback: [ self addSetGroupsForExerciseCount: 4 ];
56
Removed:
with: 'Add giant set'.
57
Removed:
html outlineButton
58
Removed:
beDanger;
59
Removed:
beSmall;
60
Removed:
callback: [ modelObject setGroups removeAll ];
61
Removed:
with: 'Remove all' ]
62
Removed:
]
src/MyFitnessTracker/MFTSetsDialog.class.st
@@ -1,10 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTSetsDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'sets'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
src/MyFitnessTracker/MFTStringDialog.class.st
@@ -1,58 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTStringDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'string'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'as yet unclassified' }
13
Removed:
MFTStringDialog class >> edit: aString [
14
Removed:
15
Removed:
^ self new
16
Removed:
action: #edit;
17
Removed:
string: aString;
18
Removed:
yourself
19
Removed:
]
20
Removed:
21
Removed:
{ #category : 'as yet unclassified' }
22
Removed:
MFTStringDialog >> initialize [
23
Removed:
24
Removed:
super initialize.
25
Removed:
string := String new
26
Removed:
]
27
Removed:
28
Removed:
{ #category : 'as yet unclassified' }
29
Removed:
MFTStringDialog >> renderContentOn: html [
30
Removed:
31
Removed:
self renderDialogOn: html with: [
32
Removed:
html textInput formControl
33
Removed:
value: string;
34
Removed:
callback: [ :value | string := value ] ]
35
Removed:
]
36
Removed:
37
Removed:
{ #category : 'as yet unclassified' }
38
Removed:
MFTStringDialog >> renderSubmitOn: html [
39
Removed:
40
Removed:
html buttonGroup: [
41
Removed:
html formButton
42
Removed:
beSubmit;
43
Removed:
bePrimary;
44
Removed:
callback: [ self answer: string ];
45
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]) ]
46
Removed:
]
47
Removed:
48
Removed:
{ #category : 'as yet unclassified' }
49
Removed:
MFTStringDialog >> string [
50
Removed:
51
Removed:
^ string
52
Removed:
]
53
Removed:
54
Removed:
{ #category : 'as yet unclassified' }
55
Removed:
MFTStringDialog >> string: anObject [
56
Removed:
57
Removed:
string := anObject
58
Removed:
]
src/MyFitnessTracker/MFTUserLandingPage.class.st
@@ -1,15 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTUserLandingPage',
3
Removed:
#superclass : 'MFTLandingPage',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'as yet unclassified' }
10
Removed:
MFTUserLandingPage >> defaultCards [
11
Removed:
12
Removed:
^ {
13
Removed:
MFTMainUserCard new.
14
Removed:
MFTWorkoutsCard new }
15
Removed:
]
src/MyFitnessTracker/MFTUserTask.class.st
@@ -1,85 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTUserTask',
3
Removed:
#superclass : 'WATask',
4
Removed:
#instVars : [
5
Removed:
'user',
6
Removed:
'action'
7
Removed:
],
8
Removed:
#category : 'MyFitnessTracker-Tasks',
9
Removed:
#package : 'MyFitnessTracker',
10
Removed:
#tag : 'Tasks'
11
Removed:
}
12
Removed:
13
Removed:
{ #category : 'actions' }
14
Removed:
MFTUserTask class >> edit: aMFTUser [
15
Removed:
16
Removed:
^ self new
17
Removed:
user: aMFTUser;
18
Removed:
action: #edit;
19
Removed:
yourself
20
Removed:
]
21
Removed:
22
Removed:
{ #category : 'accessing' }
23
Removed:
MFTUserTask >> action [
24
Removed:
25
Removed:
^ action
26
Removed:
]
27
Removed:
28
Removed:
{ #category : 'accessing' }
29
Removed:
MFTUserTask >> action: anObject [
30
Removed:
31
Removed:
action := anObject
32
Removed:
]
33
Removed:
34
Removed:
{ #category : 'running' }
35
Removed:
MFTUserTask >> go [
36
Removed:
37
Removed:
user username: (self call: (MFTStringDialog new
38
Removed:
action: action;
39
Removed:
heading: 'username';
40
Removed:
string: user username;
41
Removed:
completion: 20)).
42
Removed:
user passwordHashFromString: (self call: (MFTStringDialog new
43
Removed:
action: action;
44
Removed:
heading: 'password';
45
Removed:
completion: 40)).
46
Removed:
user dateOfBirth: (self call: (MFTDateDialog new
47
Removed:
action: action;
48
Removed:
heading: 'date of birth';
49
Removed:
date: user dateOfBirth;
50
Removed:
completion: 60)).
51
Removed:
user realFirstName: (self call: (MFTStringDialog new
52
Removed:
action: action;
53
Removed:
heading: 'real first name';
54
Removed:
string: user realFirstName;
55
Removed:
completion: 80)).
56
Removed:
user realLastName: (self call: (MFTStringDialog new
57
Removed:
heading: 'real last name';
58
Removed:
string: user realLastName;
59
Removed:
completion: 100)).
60
Removed:
61
Removed:
(self call: (MFTConfirmDialog new
62
Removed:
action: action;
63
Removed:
component: user))
64
Removed:
ifTrue: [ self answer: user ]
65
Removed:
ifFalse: [ self answer: nil ]
66
Removed:
]
67
Removed:
68
Removed:
{ #category : 'initialization' }
69
Removed:
MFTUserTask >> initialize [
70
Removed:
71
Removed:
super initialize.
72
Removed:
user := MFTUser new
73
Removed:
]
74
Removed:
75
Removed:
{ #category : 'accessing' }
76
Removed:
MFTUserTask >> user [
77
Removed:
78
Removed:
^ user
79
Removed:
]
80
Removed:
81
Removed:
{ #category : 'accessing' }
82
Removed:
MFTUserTask >> user: anObject [
83
Removed:
84
Removed:
user := anObject
85
Removed:
]
src/MyFitnessTracker/MFTWeightDialog.class.st
@@ -1,76 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWeightDialog',
3
Removed:
#superclass : 'MFTDialog',
4
Removed:
#instVars : [
5
Removed:
'weight'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Dialogs',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Dialogs'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'actions' }
13
Removed:
MFTWeightDialog class >> edit: aMFTWeight [
14
Removed:
15
Removed:
^ self new
16
Removed:
action: #edit;
17
Removed:
weight: aMFTWeight;
18
Removed:
yourself
19
Removed:
]
20
Removed:
21
Removed:
{ #category : 'initialization' }
22
Removed:
MFTWeightDialog >> initialize [
23
Removed:
24
Removed:
super initialize.
25
Removed:
weight := MFTWeight new
26
Removed:
]
27
Removed:
28
Removed:
{ #category : 'rendering' }
29
Removed:
MFTWeightDialog >> renderContentOn: html [
30
Removed:
31
Removed:
self renderDialogOn: html with: [
32
Removed:
html formGroup: [
33
Removed:
html label: 'Date'.
34
Removed:
html dateInput5 formControl
35
Removed:
value: weight date yyyymmdd;
36
Removed:
callback: [ :value |
37
Removed:
weight date: (Date readFrom: value pattern: 'yyyy-mm-dd') ] ].
38
Removed:
html formGroup: [
39
Removed:
html label: 'Weight'.
40
Removed:
html numberInput formControl
41
Removed:
value: (weight value = 0
42
Removed:
ifTrue: [ nil ]
43
Removed:
ifFalse: [ weight value ]);
44
Removed:
placeholder: weight value;
45
Removed:
callback: [ :value |
46
Removed:
value ifNotEmpty: [ weight value: value asNumber ] ] ] ]
47
Removed:
]
48
Removed:
49
Removed:
{ #category : 'rendering' }
50
Removed:
MFTWeightDialog >> renderSubmitOn: html [
51
Removed:
52
Removed:
html buttonGroup: [
53
Removed:
html formButton
54
Removed:
beSubmit;
55
Removed:
bePrimary;
56
Removed:
callback: [
57
Removed:
self answer: (weight value ifNotNil: [ weight ] ifNil: [ nil ]) ];
58
Removed:
with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
59
Removed:
html formButton
60
Removed:
beSubmit;
61
Removed:
beSecondary;
62
Removed:
callback: [ self answer: nil ];
63
Removed:
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
64
Removed:
]
65
Removed:
66
Removed:
{ #category : 'accessing' }
67
Removed:
MFTWeightDialog >> weight [
68
Removed:
69
Removed:
^ weight
70
Removed:
]
71
Removed:
72
Removed:
{ #category : 'accessing' }
73
Removed:
MFTWeightDialog >> weight: aMFTWeight [
74
Removed:
75
Removed:
weight := aMFTWeight
76
Removed:
]
src/MyFitnessTracker/MFTWeightVsTimeGraph.class.st
@@ -1,29 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWeightVsTimeGraph',
3
Removed:
#superclass : 'MFTGraph',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'rendering' }
10
Removed:
MFTWeightVsTimeGraph >> renderContentOn: html [
11
Removed:
12
Removed:
labels := self session user weights collect: [ :weight |
13
Removed:
weight date yyyymmdd ].
14
Removed:
data := self session user weights collect: #value.
15
Removed:
16
Removed:
html canvas id: 'weightVsTimeChart'.
17
Removed:
html script:
18
Removed:
'var ctx = document.getElementById("weightVsTimeChart").getContext("2d");
19
Removed:
var weightChart = new Chart(ctx, {
20
Removed:
type: "line",
21
Removed:
data: {
22
Removed:
labels: ' , labels asJavascript , ',
23
Removed:
datasets: [{
24
Removed:
label: "weight",
25
Removed:
data: ' , data asJavascript , ',
26
Removed:
}]
27
Removed:
},
28
Removed:
});'
29
Removed:
]
src/MyFitnessTracker/MFTWeightsCard.class.st
@@ -1,54 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWeightsCard',
3
Removed:
#superclass : 'MFTCard',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'adding' }
10
Removed:
MFTWeightsCard >> addWeight [
11
Removed:
12
Removed:
| newWeight |
13
Removed:
newWeight := self session pageManager activePage call:
14
Removed:
MFTWeightDialog new.
15
Removed:
(self session user weights anySatisfy: [ :existingWeight |
16
Removed:
newWeight date = existingWeight date and: [
17
Removed:
newWeight value = existingWeight value ] ]) ifTrue: [ ^ self ].
18
Removed:
19
Removed:
self session user isLoggedIn
20
Removed:
ifTrue: [ self session db insert: newWeight ]
21
Removed:
ifFalse: [ self session user weights add: newWeight ]
22
Removed:
]
23
Removed:
24
Removed:
{ #category : 'rendering' }
25
Removed:
MFTWeightsCard >> renderContentOn: html [
26
Removed:
27
Removed:
self renderCardOn: html with: [
28
Removed:
html cardHeader: 'Weights'.
29
Removed:
html cardBody: [
30
Removed:
html paragraph: 'Great job logging your weight '
31
Removed:
, self session user weights size asString , ' times!'.
32
Removed:
html form
33
Removed:
class: 'mb-3';
34
Removed:
with: [
35
Removed:
html buttonGroup
36
Removed:
beSmall;
37
Removed:
with: [
38
Removed:
html outlineButton
39
Removed:
bePrimary;
40
Removed:
callback: [ self addWeight ];
41
Removed:
with: 'Log weight'.
42
Removed:
html outlineButton
43
Removed:
beSecondary;
44
Removed:
callback: [ self viewAnalytics ];
45
Removed:
with: 'View progress' ] ].
46
Removed:
html render: MFTWeightVsTimeGraph new ] ]
47
Removed:
]
48
Removed:
49
Removed:
{ #category : 'as yet unclassified' }
50
Removed:
MFTWeightsCard >> viewAnalytics [
51
Removed:
52
Removed:
self session pageManager activePage call:
53
Removed:
(MFTAnalyticsPage for: #weights)
54
Removed:
]
src/MyFitnessTracker/MFTWeightsLog.class.st
@@ -1,141 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWeightsLog',
3
Removed:
#superclass : 'MFTLog',
4
Removed:
#instVars : [
5
Removed:
'weights'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'adding' }
13
Removed:
MFTWeightsLog >> addWeight [
14
Removed:
15
Removed:
| newWeight |
16
Removed:
newWeight := self call: MFTWeightDialog new.
17
Removed:
newWeight ifNil: [ ^ self ].
18
Removed:
(weights anySatisfy: [ :existingWeight |
19
Removed:
existingWeight date = newWeight date and: [
20
Removed:
existingWeight value = newWeight value ] ]) ifTrue: [ ^ self ].
21
Removed:
22
Removed:
self session user isLoggedIn
23
Removed:
ifTrue: [ self session db insert: newWeight ]
24
Removed:
ifFalse: [ weights add: newWeight ]
25
Removed:
]
26
Removed:
27
Removed:
{ #category : 'accessing' }
28
Removed:
MFTWeightsLog >> editWeight: oldWeight [
29
Removed:
30
Removed:
| newWeight |
31
Removed:
newWeight := self call: (MFTWeightDialog edit: oldWeight copy).
32
Removed:
(oldWeight value = newWeight value and: [
33
Removed:
oldWeight date = newWeight date ]) ifTrue: [ ^ self ].
34
Removed:
35
Removed:
self session user isLoggedIn
36
Removed:
ifTrue: [ self session db update: oldWeight with: newWeight ]
37
Removed:
ifFalse: [
38
Removed:
weights
39
Removed:
remove: oldWeight;
40
Removed:
add: newWeight ]
41
Removed:
]
42
Removed:
43
Removed:
{ #category : 'initialization' }
44
Removed:
MFTWeightsLog >> initialize [
45
Removed:
46
Removed:
super initialize.
47
Removed:
title := 'weights'.
48
Removed:
weights := OrderedCollection new
49
Removed:
]
50
Removed:
51
Removed:
{ #category : 'as yet unclassified' }
52
Removed:
MFTWeightsLog >> randomWittyCTA [
53
Removed:
54
Removed:
| listOfStrings randomString |
55
Removed:
listOfStrings := #( 'First time?' 'Get your sweat on!'
56
Removed:
'Go go go!' 'Go get ''em, Tiger!' ).
57
Removed:
randomString := listOfStrings atRandom.
58
Removed:
^ randomString
59
Removed:
]
60
Removed:
61
Removed:
{ #category : 'accessing' }
62
Removed:
MFTWeightsLog >> removeWeight: aMFTWeight [
63
Removed:
64
Removed:
self session user isLoggedIn
65
Removed:
ifTrue: [ self session db delete: aMFTWeight ]
66
Removed:
ifFalse: [ weights remove: aMFTWeight ]
67
Removed:
]
68
Removed:
69
Removed:
{ #category : 'rendering' }
70
Removed:
MFTWeightsLog >> renderActionsButtonGroupOn: html [
71
Removed:
72
Removed:
html form: [
73
Removed:
html buttonGroup: [
74
Removed:
html formButton
75
Removed:
bePrimary;
76
Removed:
callback: [ self addWeight ];
77
Removed:
with: 'Log weight'.
78
Removed:
html formButton
79
Removed:
beSecondary;
80
Removed:
callback: [ self viewAnalytics ];
81
Removed:
with: 'View progress' ] ]
82
Removed:
]
83
Removed:
84
Removed:
{ #category : 'rendering' }
85
Removed:
MFTWeightsLog >> renderContentOn: html [
86
Removed:
87
Removed:
weights := self session user weights sort: [ :a :b | a date > b date ].
88
Removed:
self renderLogOn: html with: [
89
Removed:
weights ifEmpty: [
90
Removed:
html jumbotron: [ html heading: self randomWittyCTA ] ].
91
Removed:
self renderActionsButtonGroupOn: html.
92
Removed:
weights ifNotEmpty: [ self renderWeightsAsTableOn: html ] ]
93
Removed:
]
94
Removed:
95
Removed:
{ #category : 'rendering' }
96
Removed:
MFTWeightsLog >> renderWeightActionsButtonGroupOn: html for: aMFTWeight [
97
Removed:
98
Removed:
html form: [
99
Removed:
html buttonGroup
100
Removed:
beSmall;
101
Removed:
with: [
102
Removed:
html formButton
103
Removed:
beSecondary;
104
Removed:
formControl;
105
Removed:
callback: [ self editWeight: aMFTWeight ];
106
Removed:
with: 'Edit'.
107
Removed:
html formButton
108
Removed:
beDanger;
109
Removed:
formControl;
110
Removed:
callback: [ self removeWeight: aMFTWeight ];
111
Removed:
with: 'Delete' ] ]
112
Removed:
]
113
Removed:
114
Removed:
{ #category : 'rendering' }
115
Removed:
MFTWeightsLog >> renderWeightsAsTableOn: html [
116
Removed:
117
Removed:
html table
118
Removed:
class: 'table table-striped';
119
Removed:
with: [
120
Removed:
html tableHead: [
121
Removed:
html
122
Removed:
tableHeading;
123
Removed:
tableHeading: 'Date'.
124
Removed:
html tableHeading
125
Removed:
style: 'text-align: right';
126
Removed:
with: 'Value' ].
127
Removed:
weights do: [ :weight |
128
Removed:
html tableRow: [
129
Removed:
html tableData: [
130
Removed:
self renderWeightActionsButtonGroupOn: html for: weight ].
131
Removed:
html tableData: weight date yyyymmdd.
132
Removed:
html tableData
133
Removed:
style: 'text-align: right';
134
Removed:
with: weight value ] ] ]
135
Removed:
]
136
Removed:
137
Removed:
{ #category : 'adding' }
138
Removed:
MFTWeightsLog >> viewAnalytics [
139
Removed:
140
Removed:
self call: (MFTAnalyticsPage for: #weights)
141
Removed:
]
src/MyFitnessTracker/MFTWidget.class.st
@@ -1,46 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWidget',
3
Removed:
#superclass : 'SBSComponent',
4
Removed:
#instVars : [
5
Removed:
'title'
6
Removed:
],
7
Removed:
#category : 'MyFitnessTracker-Components',
8
Removed:
#package : 'MyFitnessTracker',
9
Removed:
#tag : 'Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : 'accessing' }
13
Removed:
MFTWidget >> defaultDescription [
14
Removed:
15
Removed:
^ 'I am a ' , self class name
16
Removed:
]
17
Removed:
18
Removed:
{ #category : 'initialization' }
19
Removed:
MFTWidget >> initialize [
20
Removed:
21
Removed:
super initialize.
22
Removed:
title := String new
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'rendering' }
26
Removed:
MFTWidget >> renderContentOn: html [
27
Removed:
28
Removed:
html div: [
29
Removed:
title ifNotEmpty: [
30
Removed:
html heading
31
Removed:
level5;
32
Removed:
with: self title ].
33
Removed:
html paragraph: self defaultDescription ]
34
Removed:
]
35
Removed:
36
Removed:
{ #category : 'accessing' }
37
Removed:
MFTWidget >> title [
38
Removed:
39
Removed:
^ title
40
Removed:
]
41
Removed:
42
Removed:
{ #category : 'accessing' }
43
Removed:
MFTWidget >> title: aString [
44
Removed:
45
Removed:
title := aString
46
Removed:
]
src/MyFitnessTracker/MFTWorkoutForm.class.st
@@ -1,45 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWorkoutForm',
3
Removed:
#superclass : 'MFTForm',
4
Removed:
#category : 'MyFitnessTracker-Forms',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Forms'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'as yet unclassified' }
10
Removed:
MFTWorkoutForm class >> edit: aMFTWorkout [
11
Removed:
12
Removed:
^ self new
13
Removed:
modelObject: aMFTWorkout;
14
Removed:
yourself
15
Removed:
]
16
Removed:
17
Removed:
{ #category : 'initialization' }
18
Removed:
MFTWorkoutForm >> initialize [
19
Removed:
20
Removed:
super initialize.
21
Removed:
modelObject := MFTWorkout new.
22
Removed:
heading := 'Workout'
23
Removed:
]
24
Removed:
25
Removed:
{ #category : 'rendering' }
26
Removed:
MFTWorkoutForm >> renderContentOn: html [
27
Removed:
28
Removed:
self renderFormOn: html with: [
29
Removed:
html
30
Removed:
render: (MFTFormComponent set: #date);
31
Removed:
render: (MFTFormComponent set: #setGroups);
32
Removed:
render: (MFTFormComponent set: #primaryMuscleGroup);
33
Removed:
render: (MFTFormComponent set: #secondaryMuscleGroups) ]
34
Removed:
]
35
Removed:
36
Removed:
{ #category : 'as yet unclassified' }
37
Removed:
MFTWorkoutForm >> submitForm [
38
Removed:
39
Removed:
modelObject setGroups ifEmpty: [
40
Removed:
(self call:
41
Removed:
(MFTConfirmForm new modelObject: 'No exercises were specified.'))
42
Removed:
ifFalse: [ self answer: nil ] ].
43
Removed:
44
Removed:
self answer: modelObject
45
Removed:
]
src/MyFitnessTracker/MFTWorkoutTask.class.st
@@ -1,101 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWorkoutTask',
3
Removed:
#superclass : 'WATask',
4
Removed:
#instVars : [
5
Removed:
'workout',
6
Removed:
'action'
7
Removed:
],
8
Removed:
#category : 'MyFitnessTracker-Tasks',
9
Removed:
#package : 'MyFitnessTracker',
10
Removed:
#tag : 'Tasks'
11
Removed:
}
12
Removed:
13
Removed:
{ #category : 'actions' }
14
Removed:
MFTWorkoutTask class >> edit: aMFTWorkout [
15
Removed:
16
Removed:
^ self new
17
Removed:
workout: aMFTWorkout;
18
Removed:
action: #edit;
19
Removed:
yourself
20
Removed:
]
21
Removed:
22
Removed:
{ #category : 'accessing' }
23
Removed:
MFTWorkoutTask >> action [
24
Removed:
25
Removed:
^ action
26
Removed:
]
27
Removed:
28
Removed:
{ #category : 'accessing' }
29
Removed:
MFTWorkoutTask >> action: anObject [
30
Removed:
31
Removed:
action := anObject
32
Removed:
]
33
Removed:
34
Removed:
{ #category : 'running' }
35
Removed:
MFTWorkoutTask >> go [
36
Removed:
37
Removed:
| primaryMuscleGroup secondaryMuscleGroups |
38
Removed:
workout userId: self session user id.
39
Removed:
workout date: (self call: (MFTDateDialog new
40
Removed:
action: action;
41
Removed:
date: workout date;
42
Removed:
completion: 20)).
43
Removed:
workout setGroups: (self call: (MFTSetGroupsDialog new
44
Removed:
action: action;
45
Removed:
heading: 'Exercises';
46
Removed:
setGroups: workout setGroups;
47
Removed:
completion: 40)).
48
Removed:
primaryMuscleGroup := self call: (MFTPrimaryMuscleGroupDialog new
49
Removed:
action: action;
50
Removed:
primaryMuscleGroup:
51
Removed:
workout primaryMuscleGroup;
52
Removed:
completion: 60).
53
Removed:
primaryMuscleGroup ifNotNil: [
54
Removed:
workout primaryMuscleGroupId: primaryMuscleGroup id.
55
Removed:
secondaryMuscleGroups := self call:
56
Removed:
(MFTSecondaryMuscleGroupsDialog new
57
Removed:
action: action;
58
Removed:
secondaryMuscleGroups:
59
Removed:
workout secondaryMuscleGroups;
60
Removed:
completion: 80).
61
Removed:
workout secondaryMuscleGroupIds:
62
Removed:
(secondaryMuscleGroups collect: #id) ].
63
Removed:
64
Removed:
(self call: (MFTConfirmDialog new
65
Removed:
action: action;
66
Removed:
component: workout))
67
Removed:
ifTrue: [
68
Removed:
self updateSetsAttributes.
69
Removed:
self answer: workout ]
70
Removed:
ifFalse: [ self answer: nil ]
71
Removed:
]
72
Removed:
73
Removed:
{ #category : 'initialization' }
74
Removed:
MFTWorkoutTask >> initialize [
75
Removed:
76
Removed:
super initialize.
77
Removed:
workout := MFTWorkout new.
78
Removed:
action := #add
79
Removed:
]
80
Removed:
81
Removed:
{ #category : 'as yet unclassified' }
82
Removed:
MFTWorkoutTask >> updateSetsAttributes [
83
Removed:
84
Removed:
workout setGroups doWithIndex: [ :setGroup :i |
85
Removed:
setGroup sets doWithIndex: [ :set :j |
86
Removed:
set
87
Removed:
groupId: i;
88
Removed:
number: j ] ]
89
Removed:
]
90
Removed:
91
Removed:
{ #category : 'accessing' }
92
Removed:
MFTWorkoutTask >> workout [
93
Removed:
94
Removed:
^ workout
95
Removed:
]
96
Removed:
97
Removed:
{ #category : 'accessing' }
98
Removed:
MFTWorkoutTask >> workout: anObject [
99
Removed:
100
Removed:
workout := anObject
101
Removed:
]
src/MyFitnessTracker/MFTWorkoutVolumePerWeekGraph.class.st
@@ -1,46 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWorkoutVolumePerWeekGraph',
3
Removed:
#superclass : 'MFTGraph',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'as yet unclassified' }
10
Removed:
MFTWorkoutVolumePerWeekGraph >> crunchTheNumbers [
11
Removed:
"TODO: not working at all!"
12
Removed:
13
Removed:
| datasets datasetsAsJs |
14
Removed:
labels := Week dayNames.
15
Removed:
data := self session user workouts groupedBy: [ :each |
16
Removed:
each date dayOfYear // 7 ].
17
Removed:
"datasets := {
18
Removed:
{ 1. 2. 0. 0. 3. 5. 3 }.
19
Removed:
{ 3. 4. 3. 0. 0. 0. 6 } }."
20
Removed:
datasetsAsJs := data keysAndValuesDo: [ :k :v |
21
Removed:
'{ label: ' , (#w , k asString) asJavascript
22
Removed:
, ', data: ' , (v collect: #volume) asJavascript
23
Removed:
, '},' ]
24
Removed:
]
25
Removed:
26
Removed:
{ #category : 'initialization' }
27
Removed:
MFTWorkoutVolumePerWeekGraph >> initialize [
28
Removed:
29
Removed:
super initialize.
30
Removed:
self crunchTheNumbers
31
Removed:
]
32
Removed:
33
Removed:
{ #category : 'rendering' }
34
Removed:
MFTWorkoutVolumePerWeekGraph >> renderContentOn: html [
35
Removed:
36
Removed:
html canvas id: 'workoutVolumePerWeekChart'.
37
Removed:
html script:
38
Removed:
'var ctx = document.getElementById("workoutVolumePerWeekChart").getContext("2d");
39
Removed:
var weightChart = new Chart(ctx, {
40
Removed:
type: "line",
41
Removed:
data: {
42
Removed:
labels: ' , labels asJavascript , ',
43
Removed:
datasets: [ ' , data asJavascript , ' ]
44
Removed:
},
45
Removed:
});'
46
Removed:
]
src/MyFitnessTracker/MFTWorkoutVolumeVsTimeGraph.class.st
@@ -1,31 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWorkoutVolumeVsTimeGraph',
3
Removed:
#superclass : 'MFTGraph',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'rendering' }
10
Removed:
MFTWorkoutVolumeVsTimeGraph >> renderContentOn: html [
11
Removed:
12
Removed:
| workouts |
13
Removed:
workouts := self session user workouts sort: [ :a :b |
14
Removed:
a date < b date ].
15
Removed:
labels := workouts collect: [ :workout | workout date yyyymmdd ].
16
Removed:
data := workouts collect: #volume.
17
Removed:
18
Removed:
html canvas id: 'workoutVolumeVsTimeChart'.
19
Removed:
html script:
20
Removed:
'var ctx = document.getElementById("workoutVolumeVsTimeChart").getContext("2d");
21
Removed:
var weightChart = new Chart(ctx, {
22
Removed:
type: "line",
23
Removed:
data: {
24
Removed:
labels: ' , labels asJavascript , ',
25
Removed:
datasets: [{
26
Removed:
label: "total workout volume",
27
Removed:
data: ' , data asJavascript , ',
28
Removed:
}]
29
Removed:
},
30
Removed:
});'
31
Removed:
]
src/MyFitnessTracker/MFTWorkoutsCard.class.st
@@ -1,53 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : 'MFTWorkoutsCard',
3
Removed:
#superclass : 'MFTCard',
4
Removed:
#category : 'MyFitnessTracker-Components',
5
Removed:
#package : 'MyFitnessTracker',
6
Removed:
#tag : 'Components'
7
Removed:
}
8
Removed:
9
Removed:
{ #category : 'adding' }
10
Removed:
MFTWorkoutsCard >> addWorkout [
11
Removed:
12
Removed:
| newWorkout |
13
Removed:
newWorkout := self session pageManager activePage call:
14
Removed:
MFTWorkoutTask new.
15
Removed:
newWorkout ifNil: [ ^ self ].
16
Removed:
17
Removed:
self session user isLoggedIn
18
Removed:
ifTrue: [ self session db insert: newWorkout ]
19
Removed:
ifFalse: [ self session user workouts add: newWorkout ]
20
Removed:
]
21
Removed:
22
Removed:
{ #category : 'rendering' }
23
Removed:
MFTWorkoutsCard >> renderContentOn: html [
24
Removed:
25
Removed:
self renderCardOn: html with: [
26
Removed:
html cardHeader: 'Workouts'.
27
Removed:
html cardBody: [
28
Removed:
html paragraph:
29
Removed:
'Great job logging ' , self session user workouts size asString
30
Removed:
, ' workouts!'.
31
Removed:
html form
32
Removed:
class: 'my-3';
33
Removed:
with: [
34
Removed:
html buttonGroup
35
Removed:
beSmall;
36
Removed:
with: [
37
Removed:
html outlineButton
38
Removed:
bePrimary;
39
Removed:
callback: [ self addWorkout ];
40
Removed:
with: 'Log workout'.
41
Removed:
html outlineButton
42
Removed:
beSecondary;
43
Removed:
callback: [ self viewAnalytics ];
44
Removed:
with: 'View progress' ] ].
45
Removed:
html render: MFTRepsDistributionGraph new ] ]
46
Removed:
]
47
Removed:
48
Removed:
{ #category : 'adding' }
49
Removed:
MFTWorkoutsCard >> viewAnalytics [
50
Removed:
51
Removed:
self session pageManager activePage call:
52
Removed:
(MFTAnalyticsPage for: #workouts)
53
Removed:
]
src/MyFitnessTracker/MFTWorkoutsLog.class.st
@@ -1,172 +0,0 @@
1
Removed:
"
2
Removed:
I represent the list of user workouts retrieved from the database.
3
Removed:
4
Removed:
I am rendered by `MFTRootComponent` when the user views their workouts. I know about user workouts retrieved from the database.
5
Removed:
6
Removed:
* Collaborators
7
Removed:
8
Removed:
- I work with lists of `MFTWorkout`s.
9
Removed:
- I might work with `MFTExercise class`canonical exercises once the variable is implemented.
10
Removed:
11
Removed:
Public API and Key Messages
12
Removed:
13
Removed:
- message one
14
Removed:
- message two
15
Removed:
- (for bonus points) how to create instances.
16
Removed:
17
Removed:
One simple example is simply gorgeous.
18
Removed:
19
Removed:
Internal Representation and Key Implementation Points.
20
Removed:
21
Removed:
22
Removed:
Implementation Points
23
Removed:
"
24
Removed:
Class {
25
Removed:
#name : 'MFTWorkoutsLog',
26
Removed:
#superclass : 'MFTLog',
27
Removed:
#instVars : [
28
Removed:
'workouts'
29
Removed:
],
30
Removed:
#category : 'MyFitnessTracker-Components',
31
Removed:
#package : 'MyFitnessTracker',
32
Removed:
#tag : 'Components'
33
Removed:
}
34
Removed:
35
Removed:
{ #category : 'adding' }
36
Removed:
MFTWorkoutsLog >> addWorkout [
37
Removed:
38
Removed:
| newWorkout |
39
Removed:
newWorkout := self call: MFTWorkoutForm new.
40
Removed:
newWorkout ifNil: [ ^ self ].
41
Removed:
42
Removed:
self session user isLoggedIn
43
Removed:
ifTrue: [ self session db insert: newWorkout ]
44
Removed:
ifFalse: [ workouts add: newWorkout ]
45
Removed:
]
46
Removed:
47
Removed:
{ #category : 'hooks' }
48
Removed:
MFTWorkoutsLog >> children [
49
Removed:
50
Removed:
^ self session user workouts
51
Removed:
]
52
Removed:
53
Removed:
{ #category : 'accessing' }
54
Removed:
MFTWorkoutsLog >> confirmDelete: workout [
55
Removed:
56
Removed:
(self call: (MFTConfirmForm delete)) ifFalse: [ ^ self ].
57
Removed:
self session user isLoggedIn
58
Removed:
ifTrue: [ self session db delete: workout ]
59
Removed:
ifFalse: [ workouts remove: workout ]
60
Removed:
]
61
Removed:
62
Removed:
{ #category : 'as yet unclassified' }
63
Removed:
MFTWorkoutsLog >> editWorkout: oldWorkout [
64
Removed:
65
Removed:
| newWorkout |
66
Removed:
newWorkout := self call: (MFTWorkoutForm edit: oldWorkout copy).
67
Removed:
newWorkout ifNil: [ ^ self ].
68
Removed:
69
Removed:
self session user isLoggedIn
70
Removed:
ifTrue: [ self session db update: oldWorkout with: newWorkout ]
71
Removed:
ifFalse: [
72
Removed:
workouts
73
Removed:
remove: oldWorkout;
74
Removed:
add: newWorkout ]
75
Removed:
]
76
Removed:
77
Removed:
{ #category : 'initialization' }
78
Removed:
MFTWorkoutsLog >> initialize [
79
Removed:
80
Removed:
super initialize.
81
Removed:
title := 'workouts'.
82
Removed:
workouts := OrderedCollection new
83
Removed:
]
84
Removed:
85
Removed:
{ #category : 'as yet unclassified' }
86
Removed:
MFTWorkoutsLog >> randomWittyCTA [
87
Removed:
88
Removed:
| listOfStrings randomString |
89
Removed:
listOfStrings := #( 'First time?' 'Get your sweat on!'
90
Removed:
'Go go go!' 'Go get ''em, Tiger!' ).
91
Removed:
randomString := listOfStrings atRandom.
92
Removed:
^ randomString
93
Removed:
]
94
Removed:
95
Removed:
{ #category : 'rendering' }
96
Removed:
MFTWorkoutsLog >> renderActionsButtonGroupOn: html [
97
Removed:
98
Removed:
html form: [
99
Removed:
html buttonGroup: [
100
Removed:
html formButton
101
Removed:
bePrimary;
102
Removed:
callback: [ self addWorkout ];
103
Removed:
with: 'Log workout'.
104
Removed:
html formButton
105
Removed:
beSecondary;
106
Removed:
callback: [ self viewAnalytics ];
107
Removed:
with: 'View progress' ] ]
108
Removed:
]
109
Removed:
110
Removed:
{ #category : 'rendering' }
111
Removed:
MFTWorkoutsLog >> renderContentOn: html [
112
Removed:
113
Removed:
workouts := self session user workouts sort: [ :a :b |
114
Removed:
a date > b date ].
115
Removed:
self renderLogOn: html with: [
116
Removed:
workouts ifEmpty: [
117
Removed:
html jumbotron: [ html heading: self randomWittyCTA ] ].
118
Removed:
self renderActionsButtonGroupOn: html.
119
Removed:
workouts ifNotEmpty: [ self renderWorkoutsAsAccordionOn: html ] ]
120
Removed:
]
121
Removed:
122
Removed:
{ #category : 'rendering' }
123
Removed:
MFTWorkoutsLog >> renderWorkoutActionsButtonGroup: html for: workout [
124
Removed:
125
Removed:
html form: [
126
Removed:
html buttonGroup: [
127
Removed:
html formButton
128
Removed:
bePrimary;
129
Removed:
callback: [ self editWorkout: workout ];
130
Removed:
with: 'Edit'.
131
Removed:
html formButton
132
Removed:
beDanger;
133
Removed:
callback: [ self confirmDelete: workout ];
134
Removed:
with: 'Delete' ] ]
135
Removed:
]
136
Removed:
137
Removed:
{ #category : 'rendering' }
138
Removed:
MFTWorkoutsLog >> renderWorkoutsAsAccordionOn: html [
139
Removed:
140
Removed:
html accordion
141
Removed:
id: 'accordion';
142
Removed:
class: 'my-3';
143
Removed:
with: [
144
Removed:
workouts doWithIndex: [ :workout :i |
145
Removed:
html accordionItem: [
146
Removed:
html accordionHeading: [
147
Removed:
html accordionButton
148
Removed:
dataToggle: 'collapse';
149
Removed:
dataTarget: '#collapse' , i asString;
150
Removed:
with: [
151
Removed:
html text:
152
Removed:
workout date yyyymmdd , ', ' , workout date dayOfWeekName.
153
Removed:
workout primaryMuscleGroup ifNotNil: [
154
Removed:
html
155
Removed:
text: ':';
156
Removed:
space;
157
Removed:
emphasis: workout primaryMuscleGroup englishName ] ] ].
158
Removed:
html accordionCollapse
159
Removed:
id: 'collapse' , i asString;
160
Removed:
class: 'collapse';
161
Removed:
dataAttributeAt: 'bs-parent' put: '#accordion';
162
Removed:
with: [
163
Removed:
html accordionBody with: [
164
Removed:
html render: workout.
165
Removed:
self renderWorkoutActionsButtonGroup: html for: workout ] ] ] ] ]
166
Removed:
]
167
Removed:
168
Removed:
{ #category : 'adding' }
169
Removed:
MFTWorkoutsLog >> viewAnalytics [
170
Removed:
171
Removed:
self call: (MFTAnalyticsPage for: #workouts)
172
Removed:
]