[Pharo] Personal fitness tracker web app.
Megapush (hey, this isn't even v0 yet. Sorry not sorry)
Changed files
- src/MyFitnessTracker/MFTActionsCard.class.st
- src/MyFitnessTracker/MFTAnalyticsPage.class.st
- src/MyFitnessTracker/MFTAthlete.class.st
- src/MyFitnessTracker/MFTConfirmDialog.class.st
- src/MyFitnessTracker/MFTDialog.class.st
- src/MyFitnessTracker/MFTEmailSignupDialog.class.st
- src/MyFitnessTracker/MFTExercise.class.st
- src/MyFitnessTracker/MFTExercisesDialog.class.st
- src/MyFitnessTracker/MFTFooter.class.st
- src/MyFitnessTracker/MFTLearnPage.class.st
- src/MyFitnessTracker/MFTLoginRegisterLogoutComponent.class.st
- src/MyFitnessTracker/MFTMainNavigationBarComponent.class.st
- src/MyFitnessTracker/MFTMikeMentzer.class.st
- src/MyFitnessTracker/MFTPrimaryMuscleDialog.class.st
- src/MyFitnessTracker/MFTPrize.class.st
- src/MyFitnessTracker/MFTPromisesCard.class.st
- src/MyFitnessTracker/MFTPublicLandingPage.class.st
- src/MyFitnessTracker/MFTSQLite3Database.class.st
- src/MyFitnessTracker/MFTSet.class.st
- src/MyFitnessTracker/MFTStringDialog.class.st
- src/MyFitnessTracker/MFTUserLandingPage.class.st
- src/MyFitnessTracker/MFTWeight.class.st
- src/MyFitnessTracker/MFTWeightDialog.class.st
- src/MyFitnessTracker/MFTWorkoutTask.class.st
- src/MyFitnessTracker/MFTWorkoutVolumeVsTimeGraph.class.st
- src/MyFitnessTracker/MFTWorkoutsCard.class.st
src/MyFitnessTracker/MFTActionsCard.class.st
@@ -0,0 +1,16 @@
1
Added:
Class {
2
Added:
#name : #MFTActionsCard,
3
Added:
#superclass : #MFTCard,
4
Added:
#category : #'MyFitnessTracker-Components'
5
Added:
}
6
Added:
7
Added:
{ #category : #rendering }
8
Added:
MFTActionsCard >> renderContentOn: html [
9
Added:
10
Added:
self renderCardOn: html with: [
11
Added:
html form: [
12
Added:
html formButton
13
Added:
formControl;
14
Added:
callback: [ MFTWorkoutTask new ];
15
Added:
with: 'Log Workout' ] ]
16
Added:
]
src/MyFitnessTracker/MFTAnalyticsPage.class.st
@@ -21,8 +21,8 @@
21
21
^ Dictionary newFrom: {
22
22
(#workouts -> {
23
23
MFTRepsDistributionGraph new.
24
Removed:
MFTWorkoutVolumeVsTimeGraph new.
25
Removed:
MFTWorkoutVolumePerWeekGraph new }).
24
Added:
MFTWorkoutVolumeVsTimeGraph new
25
Added:
"MFTWorkoutVolumePerWeekGraph new" }).
26
26
(#weights -> { MFTWeightVsTimeGraph new }) }
27
27
]
28
28
src/MyFitnessTracker/MFTAthlete.class.st
@@ -113,5 +113,5 @@
113
113
{ #category : #accessing }
114
114
MFTAthlete >> workouts [
115
115
116
Removed:
^ workouts
116
Added:
self subclassResponsibility
117
117
]
src/MyFitnessTracker/MFTConfirmDialog.class.st
@@ -4,24 +4,6 @@
4
4
#category : #'MyFitnessTracker-Dialogs'
5
5
}
6
6
7
Removed:
{ #category : #public }
8
Removed:
MFTConfirmDialog class >> add: anObject [
9
Removed:
10
Removed:
^ self new
11
Removed:
action: #add;
12
Removed:
component: anObject;
13
Removed:
yourself
14
Removed:
]
15
Removed:
16
Removed:
{ #category : #public }
17
Removed:
MFTConfirmDialog class >> delete: anObject [
18
Removed:
19
Removed:
^ self new
20
Removed:
action: #delete;
21
Removed:
component: anObject;
22
Removed:
yourself
23
Removed:
]
24
Removed:
25
7
{ #category : #'as yet unclassified' }
26
8
MFTConfirmDialog >> heading [
27
9
src/MyFitnessTracker/MFTDialog.class.st
@@ -11,6 +11,24 @@
11
11
}
12
12
13
13
{ #category : #actions }
14
Added:
MFTDialog class >> add: anObject [
15
Added:
16
Added:
^ self new
17
Added:
action: #add;
18
Added:
component: anObject;
19
Added:
yourself
20
Added:
]
21
Added:
22
Added:
{ #category : #actions }
23
Added:
MFTDialog class >> delete: anObject [
24
Added:
25
Added:
^ self new
26
Added:
action: #delete;
27
Added:
component: anObject;
28
Added:
yourself
29
Added:
]
30
Added:
31
Added:
{ #category : #actions }
14
32
MFTDialog class >> edit: anObject [
15
33
16
34
^ self new
src/MyFitnessTracker/MFTEmailSignupDialog.class.st
@@ -24,6 +24,7 @@
24
24
paddingTopAndBottom: 3;
25
25
paddingLeftAndRight: 5;
26
26
class: 'fixed-top';
27
Added:
style: 'z-index: 1052';
27
28
with: [
28
29
html heading
29
30
level3;
@@ -50,15 +51,14 @@
50
51
callback: [ :value | domain := value ];
51
52
placeholder: 'example.com'.
52
53
html inputGroupAppend: [
53
Removed:
html form: [
54
Removed:
html buttonGroup: [
55
Removed:
html formButton
56
Removed:
beSecondary;
57
Removed:
beSubmit;
58
Removed:
callback: [ self answer: localPart , '@' , domain ];
59
Removed:
with: 'Confirm'.
60
Removed:
html outlineButton
61
Removed:
beSecondary;
62
Removed:
callback: [ self answer: nil ];
63
Removed:
with: 'Cancel' ] ] ] ] ] ]
54
Added:
html buttonGroup: [
55
Added:
html formButton
56
Added:
beSecondary;
57
Added:
beSubmit;
58
Added:
callback: [ self answer: localPart , '@' , domain ];
59
Added:
with: 'Confirm'.
60
Added:
html outlineButton
61
Added:
beSecondary;
62
Added:
callback: [ self answer: nil ];
63
Added:
with: 'Cancel' ] ] ] ] ]
64
64
]
src/MyFitnessTracker/MFTExercise.class.st
@@ -245,12 +245,6 @@
245
245
primaryMuscle := anObject
246
246
]
247
247
248
Removed:
{ #category : #rendering }
249
Removed:
MFTExercise >> renderContentOn: html [
250
Removed:
251
Removed:
html text: englishName
252
Removed:
]
253
Removed:
254
248
{ #category : #'as yet unclassified' }
255
249
MFTExercise >> repsBySet [
256
250
@@ -284,7 +278,5 @@
284
278
{ #category : #'as yet unclassified' }
285
279
MFTExercise >> volume [
286
280
287
Removed:
^ sets
288
Removed:
ifNotEmpty: [ (sets collect: [ :set | set volume ]) sum ]
289
Removed:
ifEmpty: 0
281
Added:
^ sets ifNotEmpty: [ (sets collect: #volume) sum ] ifEmpty: 0
290
282
]
src/MyFitnessTracker/MFTExercisesDialog.class.st
@@ -7,12 +7,22 @@
7
7
#category : #'MyFitnessTracker-Dialogs'
8
8
}
9
9
10
Added:
{ #category : #actions }
11
Added:
MFTExercisesDialog class >> edit: aMFTExerciseCollection [
12
Added:
13
Added:
self halt.
14
Added:
^ self new
15
Added:
exercises: aMFTExerciseCollection;
16
Added:
action: #edit;
17
Added:
yourself
18
Added:
]
19
Added:
10
20
{ #category : #adding }
11
21
MFTExercisesDialog >> addExercise [
12
22
13
23
| exercise |
14
24
exercise := self call: MFTExerciseDialog new.
15
Removed:
exercise ifNotNil: [ exercises add: exercise ]
25
Added:
exercise englishName ifNotEmpty: [ exercises add: exercise ]
16
26
]
17
27
18
28
{ #category : #accessing }
src/MyFitnessTracker/MFTFooter.class.st
@@ -7,7 +7,20 @@
7
7
{ #category : #rendering }
8
8
MFTFooter >> renderContentOn: html [
9
9
10
Removed:
html card
10
Added:
self
11
Added:
renderFooterOn: html
12
Added:
with: [ html text: 'I am the default footer' ]
13
Added:
]
14
Added:
15
Added:
{ #category : #rendering }
16
Added:
MFTFooter >> renderFooterOn: html with: aBlock [
17
Added:
18
Added:
html div
11
19
marginTop: 3;
12
Removed:
with: [ html cardHeader: 'I am the default footer' ]
20
Added:
paddingTopAndBottom: 3;
21
Added:
class: 'bg-light';
22
Added:
with: [
23
Added:
html container
24
Added:
class: 'd-flex justify-content-center';
25
Added:
with: [ aBlock value ] ]
13
26
]
src/MyFitnessTracker/MFTLearnPage.class.st
@@ -26,7 +26,7 @@
26
26
html breadcrumb: [
27
27
html breadcrumbItem: [
28
28
html anchor
29
Removed:
callback: [ self session pageManager activePage: #learn ];
29
Added:
callback: [ self session pageManager setActivePageAt: #learn ];
30
30
with: 'Home' ].
31
31
html breadcrumbItem: [
32
32
html anchor
src/MyFitnessTracker/MFTLoginRegisterLogoutComponent.class.st
@@ -9,27 +9,13 @@
9
9
10
10
{ #category : #'as yet unclassified' }
11
11
MFTLoginRegisterLogoutComponent >> attemptLogin: aMFTUser [
12
Removed:
"Attempt to match aMFTUser passwordHash against an existing user password_hash in the database.
12
Added:
"Attempt to match aMFTUser passwordHash against an existing user password_hash in the database."
13
13
14
Removed:
If a matching user is found, hydrate a MFTUser from the existingUser.
15
Removed:
If no matching user is found, kill the connection."
16
Removed:
17
Removed:
| connection existingUser passwordHash |
18
Removed:
connection := MFTSQLite3Database defaultConnection.
19
Removed:
connection open.
20
Removed:
existingUser := ((connection
21
Removed:
execute: 'SELECT * FROM users WHERE username = ?'
22
Removed:
value: aMFTUser username) onlyRow: [
23
Removed:
Dictionary new ]) asDictionary.
24
Removed:
connection close.
25
Removed:
passwordHash := existingUser
26
Removed:
at: #password_hash
27
Removed:
ifAbsent: [ String empty ].
28
Removed:
aMFTUser passwordHash = passwordHash
29
Removed:
ifTrue: [
30
Removed:
self session login: (MFTUser newFromDictionary: existingUser) ]
31
Removed:
ifFalse: [
32
Removed:
self session pageManager activePage inform: 'Login failed' ]
14
Added:
| existingUser |
15
Added:
existingUser := MFTSQLite3Database getExistingUser: aMFTUser.
16
Added:
aMFTUser passwordHash = existingUser passwordHash
17
Added:
ifTrue: [ self session login: aMFTUser ]
18
Added:
ifFalse: [ self inform: 'Login failed' ]
33
19
]
34
20
35
21
{ #category : #initialization }
@@ -42,7 +28,7 @@
42
28
{ #category : #'as yet unclassified' }
43
29
MFTLoginRegisterLogoutComponent >> logout [
44
30
45
Removed:
self session unregistered
31
Added:
self session logout
46
32
]
47
33
48
34
{ #category : #rendering }
@@ -139,7 +125,7 @@
139
125
html modalHeader: [
140
126
html modalTitle
141
127
level5;
142
Removed:
with: 'Welcome on board!'.
128
Added:
with: 'Welcome aboard!'.
143
129
html modalCloseButton ].
144
130
html modalBody with: [
145
131
html formGroup: [
src/MyFitnessTracker/MFTMainNavigationBarComponent.class.st
@@ -45,7 +45,7 @@
45
45
dataTarget: '#navbarCollapsed';
46
46
with: [ html navigationBarTogglerIcon ].
47
47
html navigationBarBrand
48
Removed:
callback: [ pm activePage: #landing ];
48
Added:
callback: [ pm setActivePageAt: #landing ];
49
49
with: 'MyFitnessTracker'.
50
50
html navigationBarCollapse collapse
51
51
id: 'navbarCollapsed';
@@ -56,7 +56,7 @@
56
56
beActiveIf: [ pm activePage = value ];
57
57
with: [
58
58
html navigationLink
59
Removed:
callback: [ pm activePage: key ];
59
Added:
callback: [ pm setActivePageAt: key ];
60
60
with: value title capitalized ] ] ] ].
61
61
html div: [ html render: loginRegisterLogout ]
62
62
]
src/MyFitnessTracker/MFTMikeMentzer.class.st
@@ -12,7 +12,7 @@
12
12
nickname := 'Mike Mentzer'.
13
13
born := Date fromString: '11/15/1951'.
14
14
died := Date fromString: '06/10/2001'.
15
Removed:
bio := 'Mike Mentzer is most famous for his Heavy Duty training regimen.'.
15
Added:
bio := 'Mike Mentzer is most famous for his Heavy Duty training regimen.'
16
16
]
17
17
18
18
{ #category : #accessing }
src/MyFitnessTracker/MFTPrimaryMuscleDialog.class.st
@@ -34,7 +34,7 @@
34
34
selectedMuscle := allMuscles
35
35
detect: [ :each |
36
36
each englishName = primaryMuscle englishName ]
37
Removed:
ifNone: [ nil ].
37
Added:
ifNone: [ MFTMuscleGroup new ].
38
38
39
39
self renderDialogOn: html with: [
40
40
html formGroup: [
@@ -42,7 +42,7 @@
42
42
list: allMuscles;
43
43
selected: selectedMuscle;
44
44
labels: [ :muscleGroup | muscleGroup englishName ];
45
Removed:
callback: [ :group | component := group ] ] ]
45
Added:
callback: [ :group | primaryMuscle := group ] ] ]
46
46
]
47
47
48
48
{ #category : #rendering }
src/MyFitnessTracker/MFTPrize.class.st
@@ -62,9 +62,9 @@
62
62
]
63
63
64
64
{ #category : #accessing }
65
Removed:
MFTPrize >> place: anObject [
65
Added:
MFTPrize >> place: aValue [
66
66
67
Removed:
place := anObject
67
Added:
place := aValue asInteger
68
68
]
69
69
70
70
{ #category : #accessing }
@@ -74,7 +74,7 @@
74
74
]
75
75
76
76
{ #category : #accessing }
77
Removed:
MFTPrize >> year: anObject [
77
Added:
MFTPrize >> year: aDate [
78
78
79
Removed:
year := anObject
79
Added:
year := aDate
80
80
]
src/MyFitnessTracker/MFTPromisesCard.class.st
@@ -23,6 +23,6 @@
23
23
with: [
24
24
html outlineButton
25
25
bePrimary;
26
Removed:
callback: [ self session pageManager activePage: #about ];
26
Added:
callback: [ self session pageManager setActivePageAt: #about ];
27
27
with: 'Why did I create this web app?' ] ] ]
28
28
]
src/MyFitnessTracker/MFTPublicLandingPage.class.st
@@ -19,5 +19,5 @@
19
19
20
20
super initialize.
21
21
title := 'home'.
22
Removed:
cards := self defaultCards.
22
Added:
cards := self defaultCards
23
23
]
src/MyFitnessTracker/MFTSQLite3Database.class.st
@@ -80,11 +80,33 @@
80
80
close
81
81
]
82
82
83
Added:
{ #category : #'as yet unclassified' }
84
Added:
MFTSQLite3Database class >> getExistingUser: aMFTUser [
85
Added:
86
Added:
| connection existingUserDict |
87
Added:
connection := self defaultConnection.
88
Added:
connection open.
89
Added:
existingUserDict := ((connection
90
Added:
execute: 'SELECT *
91
Added:
FROM users
92
Added:
WHERE users.username = ?
93
Added:
AND users.password_hash = ?'
94
Added:
with: {
95
Added:
aMFTUser username.
96
Added:
aMFTUser passwordHash }) onlyRow: [
97
Added:
Dictionary new ]) asDictionary.
98
Added:
connection close.
99
Added:
existingUserDict
100
Added:
ifNotEmpty: [ ^ MFTUser newFromDictionary: existingUserDict ]
101
Added:
ifEmpty: [ ^ MFTUser new ]
102
Added:
]
103
Added:
83
104
{ #category : #'class initialization' }
84
105
MFTSQLite3Database class >> initialize [
85
106
86
Removed:
defaultLocation := (Smalltalk imageDirectory / 'mft.sqlite3') fullName.
87
Removed:
self ensureTablesExist.
107
Added:
defaultLocation := (Smalltalk imageDirectory / 'mft.sqlite3')
108
Added:
fullName.
109
Added:
self ensureTablesExist
88
110
]
89
111
90
112
{ #category : #'instance creation' }
@@ -148,7 +170,8 @@
148
170
self defaultConnection
149
171
open;
150
172
execute:
151
Removed:
'INSERT INTO users(username,password_hash,account_register_date_time) VALUES (?, ?, ?);'
173
Added:
'INSERT INTO users(username,password_hash,account_register_date_time)
174
Added:
VALUES (?, ?, ?);'
152
175
with: {
153
176
aMFTUser username.
154
177
aMFTUser passwordHash.
src/MyFitnessTracker/MFTSet.class.st
@@ -56,12 +56,12 @@
56
56
]
57
57
58
58
{ #category : #'as yet unclassified' }
59
Removed:
MFTSet class >> reps: anInteger weight: anInteger2 rpe: anInteger3 [
59
Added:
MFTSet class >> reps: aValue weight: aValue2 rpe: aValue3 [
60
60
61
61
^ self new
62
Removed:
reps: anInteger;
63
Removed:
weight: anInteger2;
64
Removed:
rpe: anInteger3;
62
Added:
reps: aValue;
63
Added:
weight: aValue2;
64
Added:
rpe: aValue3;
65
65
yourself
66
66
]
67
67
src/MyFitnessTracker/MFTStringDialog.class.st
@@ -11,6 +11,7 @@
11
11
MFTStringDialog class >> edit: aString [
12
12
13
13
^ self new
14
Added:
action: #edit;
14
15
string: aString;
15
16
yourself
16
17
]
src/MyFitnessTracker/MFTUserLandingPage.class.st
@@ -9,8 +9,7 @@
9
9
10
10
^ {
11
11
MFTMainUserCard new.
12
Removed:
MFTWorkoutsCard new.
13
Removed:
MFTCard new }
12
Added:
MFTWorkoutsCard new }
14
13
]
15
14
16
15
{ #category : #initialization }
src/MyFitnessTracker/MFTWeight.class.st
@@ -30,7 +30,6 @@
30
30
31
31
^ self new
32
32
date: (Date fromString: (aDictionary at: 'date'));
33
Removed:
user: (MFTUser newFromID: (aDictionary at: 'user_id'));
34
33
value: (aDictionary at: 'value');
35
34
yourself
36
35
]
@@ -78,7 +77,7 @@
78
77
79
78
super initialize.
80
79
date := Date today.
81
Removed:
value := 50
80
Added:
value := 0
82
81
"Default value should really be the user's most recently recorded weight's value..."
83
82
]
84
83
@@ -91,5 +90,5 @@
91
90
{ #category : #accessing }
92
91
MFTWeight >> value: aValue [
93
92
94
Removed:
value := aValue
93
Added:
value := aValue asNumber
95
94
]
src/MyFitnessTracker/MFTWeightDialog.class.st
@@ -35,8 +35,11 @@
35
35
html formGroup: [
36
36
html label: 'Weight'.
37
37
html numberInput formControl
38
Removed:
callback: [ :value | weight value: value ];
39
Removed:
value: weight value ] ]
38
Added:
value: (weight value = 0
39
Added:
ifTrue: [ nil ]
40
Added:
ifFalse: [ weight value ]);
41
Added:
placeholder: weight value;
42
Added:
callback: [ :value | weight value: value ] ] ]
40
43
]
41
44
42
45
{ #category : #rendering }
@@ -51,7 +54,7 @@
51
54
html formButton
52
55
beSubmit;
53
56
beSecondary;
54
Removed:
callback: [ self answer: nil ];
57
Added:
callback: [ self answer: MFTWeight new ];
55
58
with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
56
59
]
57
60
src/MyFitnessTracker/MFTWorkoutTask.class.st
@@ -32,7 +32,6 @@
32
32
{ #category : #running }
33
33
MFTWorkoutTask >> go [
34
34
35
Removed:
| confirmed |
36
35
workout date: (self call: (MFTDateDialog new
37
36
action: action;
38
37
date: workout date;
@@ -50,10 +49,9 @@
50
49
secondaryMuscles: workout secondaryMuscles;
51
50
completion: 80)).
52
51
53
Removed:
confirmed := self call: (MFTConfirmDialog new
54
Removed:
action: action;
55
Removed:
component: workout).
56
Removed:
confirmed
52
Added:
(self call: (MFTConfirmDialog new
53
Added:
action: action;
54
Added:
component: workout))
57
55
ifTrue: [ self answer: workout ]
58
56
ifFalse: [ self answer: nil ]
59
57
]
src/MyFitnessTracker/MFTWorkoutVolumeVsTimeGraph.class.st
@@ -4,12 +4,6 @@
4
4
#category : #'MyFitnessTracker-Components'
5
5
}
6
6
7
Removed:
{ #category : #initialization }
8
Removed:
MFTWorkoutVolumeVsTimeGraph >> initialize [
9
Removed:
10
Removed:
super initialize
11
Removed:
]
12
Removed:
13
7
{ #category : #rendering }
14
8
MFTWorkoutVolumeVsTimeGraph >> renderContentOn: html [
15
9
src/MyFitnessTracker/MFTWorkoutsCard.class.st
@@ -4,6 +4,19 @@
4
4
#category : #'MyFitnessTracker-Components'
5
5
}
6
6
7
Added:
{ #category : #adding }
8
Added:
MFTWorkoutsCard >> addWorkout [
9
Added:
10
Added:
| newWorkout |
11
Added:
newWorkout := self session pageManager activePage call:
12
Added:
MFTWorkoutTask new.
13
Added:
newWorkout ifNil: [ ^ self ].
14
Added:
15
Added:
self session user isLoggedIn
16
Added:
ifTrue: [ self session db insert: newWorkout ]
17
Added:
ifFalse: [ self session user workouts add: newWorkout ]
18
Added:
]
19
Added:
7
20
{ #category : #rendering }
8
21
MFTWorkoutsCard >> renderContentOn: html [
9
22
@@ -21,8 +34,7 @@
21
34
with: [
22
35
html outlineButton
23
36
bePrimary;
24
Removed:
callback: [
25
Removed:
self session pageManager activePage call: MFTWorkoutTask new ];
37
Added:
callback: [ self addWorkout ];
26
38
with: 'Log workout'.
27
39
html outlineButton
28
40
beSecondary;