[Pharo] Personal fitness tracker web app.
MFTCard and MFTGraph are now subclasses of MFTWidget.
Changed files
- src/MyFitnessTracker/MFTCard.class.st
- src/MyFitnessTracker/MFTGraph.class.st
- src/MyFitnessTracker/MFTHeaderComponent.class.st
- src/MyFitnessTracker/MFTPageManager.class.st
- src/MyFitnessTracker/MFTPublicLandingPage.class.st
- src/MyFitnessTracker/MFTRegisterProspectHaederComponent.class.st
- src/MyFitnessTracker/MFTRootComponent.class.st
- src/MyFitnessTracker/MFTWeightsListPage.class.st
- src/MyFitnessTracker/MFTWidget.class.st
- src/MyFitnessTracker/MFTWorkoutsListPage.class.st
src/MyFitnessTracker/MFTCard.class.st
@@ -1,22 +1,10 @@
1
1
Class {
2
2
#name : #MFTCard,
3
Removed:
#superclass : #SBSComponent,
4
Removed:
#instVars : [
5
Removed:
'title',
6
Removed:
'type'
7
Removed:
],
3
Added:
#superclass : #MFTWidget,
8
4
#category : #'MyFitnessTracker-Components'
9
5
}
10
6
11
7
{ #category : #'accessing - structure variables' }
12
Removed:
MFTCard class >> type: aType [
13
Removed:
14
Removed:
^ self new
15
Removed:
type: aType;
16
Removed:
yourself
17
Removed:
]
18
Removed:
19
Removed:
{ #category : #'accessing - structure variables' }
20
8
MFTCard class >> type: aType title: aString [
21
9
22
10
^ self new
@@ -25,64 +13,42 @@
25
13
yourself
26
14
]
27
15
28
Removed:
{ #category : #accessing }
29
Removed:
MFTCard >> defaultTitle [
30
Removed:
31
Removed:
^ 'Default Card'
32
Removed:
]
33
Removed:
34
Removed:
{ #category : #initialization }
35
Removed:
MFTCard >> initialize [
36
Removed:
37
Removed:
super initialize.
38
Removed:
title := self defaultTitle.
39
Removed:
type := #default
40
Removed:
]
41
Removed:
42
16
{ #category : #rendering }
43
Removed:
MFTCard >> renderCardOn: html type: aType [
44
Removed:
45
Removed:
html card
46
Removed:
marginTopAndBottom: 3;
47
Removed:
with: [
48
Removed:
html cardHeader: self title.
49
Removed:
self
50
Removed:
perform: ('render' , aType asCamelCase , 'CardOn:') asSymbol
51
Removed:
with: html ]
52
Removed:
]
53
Removed:
54
Removed:
{ #category : #rendering }
55
17
MFTCard >> renderContentOn: html [
56
18
57
Removed:
self renderCardOn: html type: type
19
Added:
self
20
Added:
perform: ('render' , type asCamelCase , 'CardOn:') asSymbol
21
Added:
with: html
58
22
]
59
23
60
24
{ #category : #rendering }
61
25
MFTCard >> renderDefaultCardOn: html [
62
26
63
Removed:
html cardBody: [
64
Removed:
html paragraph: 'I am the default MFTCard representation.' ]
27
Added:
html cardBody: [ html paragraph: self defaultDescription ]
65
28
]
66
29
67
30
{ #category : #rendering }
68
31
MFTCard >> renderMainPublicCardOn: html [
69
32
70
Removed:
html jumbotron
71
Removed:
class: 'p-3 bg-light';
33
Added:
html card
34
Added:
marginTopAndBottom: 3;
72
35
with: [
73
Removed:
html displayHeading
74
Removed:
level: 1;
75
Removed:
with: 'Welcome to My Fitness Tracker!'.
76
Removed:
html big textMuted with: 'Free Software never looked so fit.'.
77
Removed:
html form: [
78
Removed:
html formButton
79
Removed:
bePrimary;
80
Removed:
beLarge;
81
Removed:
class: 'mt-3';
82
Removed:
callback: [
83
Removed:
self session pageManager activePage:
84
Removed:
(self session pageManager mainPages at: 2) ];
85
Removed:
with: 'Log a workout' ] ]
36
Added:
html jumbotron
37
Added:
class: 'p-3 bg-light';
38
Added:
with: [
39
Added:
html displayHeading
40
Added:
level: 1;
41
Added:
with: 'Welcome to My Fitness Tracker!'.
42
Added:
html big textMuted with: 'Free Software never looked so fit.'.
43
Added:
html form: [
44
Added:
html formButton
45
Added:
bePrimary;
46
Added:
beLarge;
47
Added:
class: 'mt-3';
48
Added:
callback: [
49
Added:
self session pageManager activePage:
50
Added:
(self session pageManager mainPages at: 2) ];
51
Added:
with: 'Log a workout' ] ] ]
86
52
]
87
53
88
54
{ #category : #rendering }
@@ -98,171 +64,78 @@
98
64
'at'.
99
65
lastLoginDate asTimeUTC asString.
100
66
'UTC' } joinUsing: ' '.
101
Removed:
html card: [
102
Removed:
html jumbotron
103
Removed:
class: 'p-3 bg-light';
104
Removed:
with: [
105
Removed:
html displayHeading
106
Removed:
level: 1;
107
Removed:
with: [
108
Removed:
html
109
Removed:
text: 'Welcome, ';
110
Removed:
code: self session user username;
111
Removed:
text: '!' ].
112
Removed:
html big textMuted with: subtitle ] ]
67
Added:
68
Added:
69
Added:
html card
70
Added:
marginTopAndBottom: 3;
71
Added:
with: [
72
Added:
html jumbotron
73
Added:
class: 'p-3 bg-light';
74
Added:
with: [
75
Added:
html displayHeading
76
Added:
level: 1;
77
Added:
with: [
78
Added:
html
79
Added:
text: 'Welcome, ';
80
Added:
code: self session user username;
81
Added:
text: '!' ].
82
Added:
html big textMuted with: subtitle ] ]
113
83
]
114
84
115
85
{ #category : #rendering }
116
86
MFTCard >> renderPromisesCardOn: html [
117
87
118
Removed:
html cardBody: [
119
Removed:
html unorderedList: [
120
Removed:
html listItem:
121
Removed:
'Every single feature shall always be free of charge.'.
122
Removed:
html listItem:
123
Removed:
'No user data shall ever be sold, under any circumstances.'.
124
Removed:
html listItem: 'No user data shall ever be tracked.' ] ]
88
Added:
html card: [
89
Added:
html cardHeader: self title.
90
Added:
html cardBody: [
91
Added:
html unorderedList: [
92
Added:
html listItem:
93
Added:
'Every single feature shall always be free of charge.'.
94
Added:
html listItem:
95
Added:
'No personal user data shall ever be sold, under any circumstances.'.
96
Added:
html listItem:
97
Added:
'No external advertisements shall ever be displayed.' ] ] ]
125
98
]
126
99
127
100
{ #category : #rendering }
128
101
MFTCard >> renderWeightsCardOn: html [
129
102
130
Removed:
html cardBody: [
131
Removed:
html paragraph: 'Great job logging your weight '
132
Removed:
, self session user weights size asString , ' times!'.
133
Removed:
html form
134
Removed:
class: 'my-3';
135
Removed:
with: [
136
Removed:
html formButton
137
Removed:
bePrimary;
138
Removed:
beSmall;
139
Removed:
callback: [
140
Removed:
self session pageManager activePage:
141
Removed:
(self session pageManager mainPages at: 3) ];
142
Removed:
with: 'Log a weight' ].
143
Removed:
self renderWeightsGraphOn: html ]
103
Added:
html card: [
104
Added:
html cardHeader: self title.
105
Added:
html cardBody: [
106
Added:
html paragraph: 'Great job logging your weight '
107
Added:
, self session user weights size asString , ' times!'.
108
Added:
html form
109
Added:
class: 'my-3';
110
Added:
with: [
111
Added:
html formButton
112
Added:
bePrimary;
113
Added:
beSmall;
114
Added:
callback: [
115
Added:
self session pageManager activePage:
116
Added:
(self session pageManager mainPages at: 3) ];
117
Added:
with: 'Log a weight' ].
118
Added:
html render: (MFTGraph type: #weightVersusTime) ] ]
144
119
]
145
120
146
121
{ #category : #rendering }
147
Removed:
MFTCard >> renderWeightsGraphOn: html [
148
Removed:
149
Removed:
| labels data |
150
Removed:
labels := self session user weights collect: [ :weight |
151
Removed:
weight date yyyymmdd ].
152
Removed:
data := self session user weights collect: #value.
153
Removed:
html canvas
154
Removed:
id: 'weightChart';
155
Removed:
width: 4;
156
Removed:
height: 1.
157
Removed:
html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
158
Removed:
html script:
159
Removed:
'var ctx = document.getElementById("weightChart").getContext("2d");
160
Removed:
var weightChart = new Chart(ctx, {
161
Removed:
type: "line",
162
Removed:
data: {
163
Removed:
labels: ' , labels asJavascript , ',
164
Removed:
datasets: [{
165
Removed:
label: "weight",
166
Removed:
data: ' , data asJavascript , ',
167
Removed:
backgroundColor: "rgba(255, 99, 132, 0.2)",
168
Removed:
borderColor: "rgba(255, 99, 132, 1)",
169
Removed:
borderWidth: 1
170
Removed:
}]
171
Removed:
},
172
Removed:
options: {
173
Removed:
scales: {
174
Removed:
yAxes: [{
175
Removed:
ticks: {
176
Removed:
beginAtZero: true
177
Removed:
}
178
Removed:
}]
179
Removed:
}
180
Removed:
}
181
Removed:
});'
182
Removed:
]
183
Removed:
184
Removed:
{ #category : #rendering }
185
122
MFTCard >> renderWorkoutsCardOn: html [
186
123
187
Removed:
html cardBody: [
188
Removed:
html paragraph:
189
Removed:
'Great job logging ' , self session user workouts size asString
190
Removed:
, ' workouts!'.
191
Removed:
html form
192
Removed:
class: 'my-3';
193
Removed:
with: [
194
Removed:
html formButton
195
Removed:
bePrimary;
196
Removed:
beSmall;
197
Removed:
callback: [
198
Removed:
self session pageManager activePage:
199
Removed:
(self session pageManager mainPages at: 2) ];
200
Removed:
with: 'Log a workout' ].
201
Removed:
self renderWorkoutsGraphOn: html ]
202
Removed:
]
203
Removed:
204
Removed:
{ #category : #rendering }
205
Removed:
MFTCard >> renderWorkoutsGraphOn: html [
206
Removed:
207
Removed:
| exercises labels data repTally |
208
Removed:
exercises := self session user workouts flatCollect: #exercises.
209
Removed:
repTally := Dictionary new.
210
Removed:
exercises do: [ :exercise |
211
Removed:
repTally at: exercise englishName ifAbsentPut: [ 0 ].
212
Removed:
repTally
213
Removed:
at: exercise englishName
214
Removed:
put: (repTally at: exercise englishName) + exercise repsBySet sum ].
215
Removed:
labels := repTally keys.
216
Removed:
data := repTally values.
217
Removed:
html div
218
Removed:
style: 'max-width: 24rem';
219
Removed:
with: [
220
Removed:
html canvas
221
Removed:
id: 'workoutChart';
222
Removed:
width: 4;
223
Removed:
height: 1.
224
Removed:
html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
225
Removed:
html script:
226
Removed:
'var ctx = document.getElementById("workoutChart").getContext("2d");
227
Removed:
var workoutChart = new Chart(ctx, {
228
Removed:
type: "doughnut",
229
Removed:
data: {
230
Removed:
labels: ' , labels asJavascript , ',
231
Removed:
datasets: [{
232
Removed:
data: ' , data asJavascript , '
233
Removed:
}]
234
Removed:
},
235
Removed:
options: {
236
Removed:
title: {
237
Removed:
display: true,
238
Removed:
text: "Reps by exercise"
239
Removed:
}
240
Removed:
}
241
Removed:
});' ]
242
Removed:
]
243
Removed:
244
Removed:
{ #category : #accessing }
245
Removed:
MFTCard >> title [
246
Removed:
247
Removed:
^ title = self defaultTitle
248
Removed:
ifTrue: [ ^ type asCapitalizedPhrase ]
249
Removed:
ifFalse: [ ^ title ]
250
Removed:
]
251
Removed:
252
Removed:
{ #category : #accessing }
253
Removed:
MFTCard >> title: aString [
254
Removed:
255
Removed:
title := aString
256
Removed:
]
257
Removed:
258
Removed:
{ #category : #accessing }
259
Removed:
MFTCard >> type [
260
Removed:
261
Removed:
^ type
262
Removed:
]
263
Removed:
264
Removed:
{ #category : #accessing }
265
Removed:
MFTCard >> type: anObject [
266
Removed:
267
Removed:
type := anObject
124
Added:
html card: [
125
Added:
html cardHeader: self title.
126
Added:
html cardBody: [
127
Added:
html paragraph:
128
Added:
'Great job logging ' , self session user workouts size asString
129
Added:
, ' workouts!'.
130
Added:
html form
131
Added:
class: 'my-3';
132
Added:
with: [
133
Added:
html formButton
134
Added:
bePrimary;
135
Added:
beSmall;
136
Added:
callback: [
137
Added:
self session pageManager activePage:
138
Added:
(self session pageManager mainPages at: 2) ];
139
Added:
with: 'Log a workout' ].
140
Added:
html render: (MFTGraph type: #repsVersusExercises) ] ]
268
141
]
src/MyFitnessTracker/MFTGraph.class.st
@@ -0,0 +1,105 @@
1
Added:
Class {
2
Added:
#name : #MFTGraph,
3
Added:
#superclass : #MFTWidget,
4
Added:
#category : #'MyFitnessTracker-Components'
5
Added:
}
6
Added:
7
Added:
{ #category : #rendering }
8
Added:
MFTGraph >> renderContentOn: html [
9
Added:
10
Added:
html div: [
11
Added:
self
12
Added:
perform: ('render' , type asCamelCase , 'GraphOn:') asSymbol
13
Added:
with: html ]
14
Added:
]
15
Added:
16
Added:
{ #category : #rendering }
17
Added:
MFTGraph >> renderDefaultGraphOn: html [
18
Added:
19
Added:
| labels data |
20
Added:
labels := #( Monday Tuesday Wednesday Thursday Friday ).
21
Added:
data := #( 5 1 6 2 7 ).
22
Added:
html heading level5 with: self defaultDescription.
23
Added:
html canvas id: 'defaultGraph'.
24
Added:
html script:
25
Added:
'var ctx = document.getElementById("defaultGraph").getContext("2d");
26
Added:
var defaultChart = new Chart(ctx, {
27
Added:
type: "line",
28
Added:
data: {
29
Added:
labels: ' , labels asJavascript , ',
30
Added:
datasets: [{
31
Added:
label: "default data",
32
Added:
data: ' , data asJavascript , ',
33
Added:
borderWidth: 1
34
Added:
}]
35
Added:
}
36
Added:
});'
37
Added:
]
38
Added:
39
Added:
{ #category : #rendering }
40
Added:
MFTGraph >> renderRepsVersusExercisesGraphOn: html [
41
Added:
42
Added:
| exercises labels data repTally |
43
Added:
exercises := self session user workouts flatCollect: #exercises.
44
Added:
repTally := Dictionary new.
45
Added:
exercises do: [ :exercise |
46
Added:
repTally at: exercise englishName ifAbsentPut: [ 0 ].
47
Added:
repTally
48
Added:
at: exercise englishName
49
Added:
put: (repTally at: exercise englishName) + exercise repsBySet sum ].
50
Added:
labels := repTally keys.
51
Added:
data := repTally values.
52
Added:
53
Added:
html canvas id: 'workoutChart'.
54
Added:
html script:
55
Added:
'var ctx = document.getElementById("workoutChart").getContext("2d");
56
Added:
var workoutChart = new Chart(ctx, {
57
Added:
type: "doughnut",
58
Added:
data: {
59
Added:
labels: ' , labels asJavascript , ',
60
Added:
datasets: [{
61
Added:
data: ' , data asJavascript , '
62
Added:
}]
63
Added:
},
64
Added:
options: {
65
Added:
title: {
66
Added:
display: true,
67
Added:
text: "Reps by exercise"
68
Added:
}
69
Added:
}
70
Added:
});'
71
Added:
]
72
Added:
73
Added:
{ #category : #rendering }
74
Added:
MFTGraph >> renderWeightVersusTimeGraphOn: html [
75
Added:
76
Added:
| labels data |
77
Added:
labels := self session user weights collect: [ :weight |
78
Added:
weight date yyyymmdd ].
79
Added:
data := self session user weights collect: #value.
80
Added:
html canvas id: 'weightChart'.
81
Added:
html script:
82
Added:
'var ctx = document.getElementById("weightChart").getContext("2d");
83
Added:
var weightChart = new Chart(ctx, {
84
Added:
type: "line",
85
Added:
data: {
86
Added:
labels: ' , labels asJavascript , ',
87
Added:
datasets: [{
88
Added:
label: "weight",
89
Added:
data: ' , data asJavascript , ',
90
Added:
backgroundColor: "rgba(255, 99, 132, 0.2)",
91
Added:
borderColor: "rgba(255, 99, 132, 1)",
92
Added:
borderWidth: 1
93
Added:
}]
94
Added:
},
95
Added:
options: {
96
Added:
scales: {
97
Added:
yAxes: [{
98
Added:
ticks: {
99
Added:
beginAtZero: true
100
Added:
}
101
Added:
}]
102
Added:
}
103
Added:
}
104
Added:
});'
105
Added:
]
src/MyFitnessTracker/MFTHeaderComponent.class.st
@@ -0,0 +1,109 @@
1
Added:
Class {
2
Added:
#name : #MFTHeaderComponent,
3
Added:
#superclass : #SBSComponent,
4
Added:
#instVars : [
5
Added:
'email',
6
Added:
'localPart',
7
Added:
'domain'
8
Added:
],
9
Added:
#category : #'MyFitnessTracker-Components'
10
Added:
}
11
Added:
12
Added:
{ #category : #adding }
13
Added:
MFTHeaderComponent >> addUserEmail [
14
Added:
15
Added:
| emailToCheck emailPattern |
16
Added:
emailToCheck := self call: MFTEmailSignupDialog new.
17
Added:
emailPattern := '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]{3,}\.[a-zA-Z]{2,}$'
18
Added:
asRegex.
19
Added:
(emailToCheck isNil or: [ (emailPattern matches: emailToCheck) not ])
20
Added:
ifTrue: [ ^ nil ].
21
Added:
email := emailToCheck.
22
Added:
MFTSQLite3Database storeProspectEmail: email
23
Added:
]
24
Added:
25
Added:
{ #category : #rendering }
26
Added:
MFTHeaderComponent >> renderContentOn: html [
27
Added:
28
Added:
email
29
Added:
ifNil: [
30
Added:
html alert
31
Added:
beWarning;
32
Added:
beDismissible;
33
Added:
marginBottom: 0;
34
Added:
with: [
35
Added:
html closeButton dataDismiss: 'alert'.
36
Added:
html alertHeading: 'This app is still in beta.'.
37
Added:
html paragraph: [
38
Added:
html
39
Added:
text:
40
Added:
'My Fitness Tracker is still under heavy development, with improvements being made daily.';
41
Added:
space;
42
Added:
text: 'I can''t wait to share the finished product with you!' ].
43
Added:
html form with: [
44
Added:
html label
45
Added:
style: 'margin-right: 1em';
46
Added:
with: [ html strong: 'Want to know when version 1.0 is out?' ].
47
Added:
html formButton
48
Added:
beInfo;
49
Added:
callback: [ self addUserEmail ];
50
Added:
with: 'E-mail me!' ] ] ]
51
Added:
ifNotNil: [
52
Added:
html alert
53
Added:
beSuccess;
54
Added:
beDismissible;
55
Added:
marginBottom: 0;
56
Added:
with: [
57
Added:
html closeButton dataDismiss: 'alert'.
58
Added:
html text: email , ' will be notified when v1 is released!' ] ]
59
Added:
]
60
Added:
61
Added:
{ #category : #rendering }
62
Added:
MFTHeaderComponent >> renderEmailInputModalButtonOn: html [
63
Added:
64
Added:
^ html formButton
65
Added:
bePrimary;
66
Added:
beSmall;
67
Added:
dataToggle: 'modal';
68
Added:
dataTarget: '#emailInputModal';
69
Added:
with: 'E-mail me!'
70
Added:
]
71
Added:
72
Added:
{ #category : #rendering }
73
Added:
MFTHeaderComponent >> renderEmailInputModalOn: html [
74
Added:
75
Added:
html modal
76
Added:
id: 'emailInputModal';
77
Added:
fade;
78
Added:
with: [
79
Added:
html modalDialog
80
Added:
beCentered;
81
Added:
with: [
82
Added:
html modalContent: [
83
Added:
html form: [
84
Added:
html modalHeader: [
85
Added:
html modalTitle
86
Added:
level5;
87
Added:
with:
88
Added:
'You''ll be the first to know when My Fitness Tracker is ready to use.'.
89
Added:
html modalCloseButton ].
90
Added:
91
Added:
html modalBody: [
92
Added:
html inputGroup: [
93
Added:
html formTextInput
94
Added:
callback: [ :value | localPart := value ];
95
Added:
placeholder: 'my-user'.
96
Added:
html inputGroupAppend: [ html inputGroupText: '@' ].
97
Added:
html formTextInput
98
Added:
callback: [ :value | domain := value ];
99
Added:
placeholder: 'example.com' ] ].
100
Added:
101
Added:
html modalFooter: [
102
Added:
html paragraph:
103
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.'.
104
Added:
html formButton
105
Added:
bePrimary;
106
Added:
beSubmit;
107
Added:
callback: [ email := localPart , '@' , domain ];
108
Added:
with: 'Confirm' ] ] ] ] ]
109
Added:
]
src/MyFitnessTracker/MFTPageManager.class.st
@@ -99,5 +99,5 @@
99
99
{ #category : #'as yet unclassified' }
100
100
MFTPageManager >> registerProspectHeader [
101
101
102
Removed:
^ MFTRegisterProspectHaederComponent new
102
Added:
^ MFTHeaderComponent new
103
103
]
src/MyFitnessTracker/MFTPublicLandingPage.class.st
@@ -16,8 +16,12 @@
16
16
17
17
html container: [
18
18
html render: (MFTCard type: #mainPublic).
19
Removed:
html render: (MFTCard type: #workouts).
20
Removed:
html render: (MFTCard type: #weights).
21
Removed:
html render:
22
Removed:
(MFTCard type: #promises title: 'My promises to all users') ]
19
Added:
html div
20
Added:
style:
21
Added:
'display: flex; flex-wrap: wrap; justify-content: space-evenly; gap: 1em';
22
Added:
with: [
23
Added:
html render: (MFTCard type: #workouts title: 'Workouts').
24
Added:
html render: (MFTCard type: #weights title: 'Weights').
25
Added:
html render:
26
Added:
(MFTCard type: #promises title: 'My promises to all users') ] ]
23
27
]
src/MyFitnessTracker/MFTRegisterProspectHaederComponent.class.st
@@ -1,109 +0,0 @@
1
Removed:
Class {
2
Removed:
#name : #MFTRegisterProspectHaederComponent,
3
Removed:
#superclass : #SBSComponent,
4
Removed:
#instVars : [
5
Removed:
'email',
6
Removed:
'localPart',
7
Removed:
'domain'
8
Removed:
],
9
Removed:
#category : #'MyFitnessTracker-Components'
10
Removed:
}
11
Removed:
12
Removed:
{ #category : #adding }
13
Removed:
MFTRegisterProspectHaederComponent >> addUserEmail [
14
Removed:
15
Removed:
| emailToCheck emailPattern |
16
Removed:
emailToCheck := self call: MFTEmailSignupDialog new.
17
Removed:
emailPattern := '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]{3,}\.[a-zA-Z]{2,}$'
18
Removed:
asRegex.
19
Removed:
(emailToCheck isNil or: [ (emailPattern matches: emailToCheck) not ])
20
Removed:
ifTrue: [ ^ nil ].
21
Removed:
email := emailToCheck.
22
Removed:
MFTSQLite3Database storeProspectEmail: email
23
Removed:
]
24
Removed:
25
Removed:
{ #category : #rendering }
26
Removed:
MFTRegisterProspectHaederComponent >> renderContentOn: html [
27
Removed:
28
Removed:
email
29
Removed:
ifNil: [
30
Removed:
html alert
31
Removed:
beWarning;
32
Removed:
beDismissible;
33
Removed:
marginBottom: 0;
34
Removed:
with: [
35
Removed:
html closeButton dataDismiss: 'alert'.
36
Removed:
html alertHeading: 'This app is still in beta.'.
37
Removed:
html paragraph: [
38
Removed:
html
39
Removed:
text:
40
Removed:
'My Fitness Tracker is still under heavy development, with improvements being made daily.';
41
Removed:
space;
42
Removed:
text: 'I can''t wait to share the finished product with you!' ].
43
Removed:
html form with: [
44
Removed:
html label
45
Removed:
style: 'margin-right: 1em';
46
Removed:
with: [ html strong: 'Want to know when version 1.0 is out?' ].
47
Removed:
html formButton
48
Removed:
beInfo;
49
Removed:
callback: [ self addUserEmail ];
50
Removed:
with: 'E-mail me!' ] ] ]
51
Removed:
ifNotNil: [
52
Removed:
html alert
53
Removed:
beSuccess;
54
Removed:
beDismissible;
55
Removed:
marginBottom: 0;
56
Removed:
with: [
57
Removed:
html closeButton dataDismiss: 'alert'.
58
Removed:
html text: email , ' will be notified when v1 is released!' ] ]
59
Removed:
]
60
Removed:
61
Removed:
{ #category : #rendering }
62
Removed:
MFTRegisterProspectHaederComponent >> renderEmailInputModalButtonOn: html [
63
Removed:
64
Removed:
^ html formButton
65
Removed:
bePrimary;
66
Removed:
beSmall;
67
Removed:
dataToggle: 'modal';
68
Removed:
dataTarget: '#emailInputModal';
69
Removed:
with: 'E-mail me!'
70
Removed:
]
71
Removed:
72
Removed:
{ #category : #rendering }
73
Removed:
MFTRegisterProspectHaederComponent >> renderEmailInputModalOn: html [
74
Removed:
75
Removed:
html modal
76
Removed:
id: 'emailInputModal';
77
Removed:
fade;
78
Removed:
with: [
79
Removed:
html modalDialog
80
Removed:
beCentered;
81
Removed:
with: [
82
Removed:
html modalContent: [
83
Removed:
html form: [
84
Removed:
html modalHeader: [
85
Removed:
html modalTitle
86
Removed:
level5;
87
Removed:
with:
88
Removed:
'You''ll be the first to know when My Fitness Tracker is ready to use.'.
89
Removed:
html modalCloseButton ].
90
Removed:
91
Removed:
html modalBody: [
92
Removed:
html inputGroup: [
93
Removed:
html formTextInput
94
Removed:
callback: [ :value | localPart := value ];
95
Removed:
placeholder: 'my-user'.
96
Removed:
html inputGroupAppend: [ html inputGroupText: '@' ].
97
Removed:
html formTextInput
98
Removed:
callback: [ :value | domain := value ];
99
Removed:
placeholder: 'example.com' ] ].
100
Removed:
101
Removed:
html modalFooter: [
102
Removed:
html paragraph:
103
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.'.
104
Removed:
html formButton
105
Removed:
bePrimary;
106
Removed:
beSubmit;
107
Removed:
callback: [ email := localPart , '@' , domain ];
108
Removed:
with: 'Confirm' ] ] ] ] ]
109
Removed:
]
src/MyFitnessTracker/MFTRootComponent.class.st
@@ -7,7 +7,7 @@
7
7
#category : #'MyFitnessTracker-Components'
8
8
}
9
9
10
Removed:
{ #category : #initialization }
10
Added:
{ #category : #testing }
11
11
MFTRootComponent class >> canBeRoot [
12
12
13
13
^ true
@@ -73,6 +73,7 @@
73
73
{ #category : #rendering }
74
74
MFTRootComponent >> renderContentOn: html [
75
75
76
Added:
html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
76
77
html render: main.
77
78
self addBootstrapJSScriptTo: html
78
79
]
src/MyFitnessTracker/MFTWeightsListPage.class.st
@@ -72,7 +72,11 @@
72
72
html formButton
73
73
bePrimary;
74
74
callback: [ self addWeight ];
75
Removed:
with: 'Add weight' ] ]
75
Added:
with: 'Add weight'.
76
Added:
html formButton
77
Added:
beSecondary;
78
Added:
callback: [ self ];
79
Added:
with: 'View progress' ] ]
76
80
]
77
81
78
82
{ #category : #rendering }
src/MyFitnessTracker/MFTWidget.class.st
@@ -0,0 +1,91 @@
1
Added:
Class {
2
Added:
#name : #MFTWidget,
3
Added:
#superclass : #SBSComponent,
4
Added:
#instVars : [
5
Added:
'title',
6
Added:
'type'
7
Added:
],
8
Added:
#category : #'MyFitnessTracker-Components'
9
Added:
}
10
Added:
11
Added:
{ #category : #accessing }
12
Added:
MFTWidget class >> default [
13
Added:
14
Added:
^ self new
15
Added:
title: 'Default ' , self name;
16
Added:
yourself
17
Added:
]
18
Added:
19
Added:
{ #category : #'accessing - structure variables' }
20
Added:
MFTWidget class >> type: aType [
21
Added:
22
Added:
^ self new
23
Added:
type: aType;
24
Added:
yourself
25
Added:
]
26
Added:
27
Added:
{ #category : #'accessing - structure variables' }
28
Added:
MFTWidget class >> type: aType title: aString [
29
Added:
30
Added:
^ self new
31
Added:
type: aType;
32
Added:
title: aString;
33
Added:
yourself
34
Added:
]
35
Added:
36
Added:
{ #category : #accessing }
37
Added:
MFTWidget >> defaultDescription [
38
Added:
39
Added:
^ {
40
Added:
'I am a'.
41
Added:
self class name.
42
Added:
'of type'.
43
Added:
(type , '.') } joinUsing: ' '
44
Added:
]
45
Added:
46
Added:
{ #category : #initialization }
47
Added:
MFTWidget >> initialize [
48
Added:
49
Added:
super initialize.
50
Added:
title := String new.
51
Added:
type := #default
52
Added:
]
53
Added:
54
Added:
{ #category : #rendering }
55
Added:
MFTWidget >> renderContentOn: html [
56
Added:
57
Added:
html div: [
58
Added:
title ifNotEmpty: [
59
Added:
html heading
60
Added:
level5;
61
Added:
with: self title ].
62
Added:
html paragraph: ({
63
Added:
'I am a'.
64
Added:
self class name.
65
Added:
'of type'.
66
Added:
(type , '.') } joinUsing: ' ') ]
67
Added:
]
68
Added:
69
Added:
{ #category : #accessing }
70
Added:
MFTWidget >> title [
71
Added:
72
Added:
^ title ifEmpty: [ type asCapitalizedPhrase ]
73
Added:
]
74
Added:
75
Added:
{ #category : #accessing }
76
Added:
MFTWidget >> title: aString [
77
Added:
78
Added:
title := aString
79
Added:
]
80
Added:
81
Added:
{ #category : #accessing }
82
Added:
MFTWidget >> type [
83
Added:
84
Added:
^ type
85
Added:
]
86
Added:
87
Added:
{ #category : #accessing }
88
Added:
MFTWidget >> type: aTypeSymbol [
89
Added:
90
Added:
type := aTypeSymbol
91
Added:
]
src/MyFitnessTracker/MFTWorkoutsListPage.class.st
@@ -188,10 +188,10 @@
188
188
html
189
189
text:
190
190
workout date yyyymmdd , ' ('
191
Removed:
, workout date dayOfWeekName asString , ') ';
191
Added:
, workout date dayOfWeekName asString , ')';
192
192
space.
193
193
workout primaryMuscle englishName ifNotEmpty: [
194
Removed:
html strong: workout primaryMuscle englishName asString ] ] ].
194
Added:
html strong: workout primaryMuscle englishName ] ] ].
195
195
html accordionCollapse
196
196
id: 'collapse' , i asString;
197
197
class: 'collapse';