Introducing... MFTCards! :fire:

Commit
4ff53e7aa1cd6948db0c22d08af01629680ca015
Author
Marius Peter <marius.peter@tutanota.com>
Author date
Committer
Marius Peter <marius.peter@tutanota.com>
Committer date
Changed files
src/MyFitnessTracker/MFTCard.class.st
index 00000000..22373968 000000..100644
@@ -0,0 +1,268 @@
1 Added: Class {
2 Added: #name : #MFTCard,
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 - structure variables' }
12 Added: MFTCard class >> type: aType [
13 Added:
14 Added: ^ self new
15 Added: type: aType;
16 Added: yourself
17 Added: ]
18 Added:
19 Added: { #category : #'accessing - structure variables' }
20 Added: MFTCard class >> type: aType title: aString [
21 Added:
22 Added: ^ self new
23 Added: type: aType;
24 Added: title: aString;
25 Added: yourself
26 Added: ]
27 Added:
28 Added: { #category : #accessing }
29 Added: MFTCard >> defaultTitle [
30 Added:
31 Added: ^ 'Default Card'
32 Added: ]
33 Added:
34 Added: { #category : #initialization }
35 Added: MFTCard >> initialize [
36 Added:
37 Added: super initialize.
38 Added: title := self defaultTitle.
39 Added: type := #default
40 Added: ]
41 Added:
42 Added: { #category : #rendering }
43 Added: MFTCard >> renderCardOn: html type: aType [
44 Added:
45 Added: html card
46 Added: marginTopAndBottom: 3;
47 Added: with: [
48 Added: html cardHeader: self title.
49 Added: self
50 Added: perform: ('render' , aType asCamelCase , 'CardOn:') asSymbol
51 Added: with: html ]
52 Added: ]
53 Added:
54 Added: { #category : #rendering }
55 Added: MFTCard >> renderContentOn: html [
56 Added:
57 Added: self renderCardOn: html type: type
58 Added: ]
59 Added:
60 Added: { #category : #rendering }
61 Added: MFTCard >> renderDefaultCardOn: html [
62 Added:
63 Added: html cardBody: [
64 Added: html paragraph: 'I am the default MFTCard representation.' ]
65 Added: ]
66 Added:
67 Added: { #category : #rendering }
68 Added: MFTCard >> renderMainPublicCardOn: html [
69 Added:
70 Added: html jumbotron
71 Added: class: 'p-3 bg-light';
72 Added: with: [
73 Added: html displayHeading
74 Added: level: 1;
75 Added: with: 'Welcome to My Fitness Tracker!'.
76 Added: html big textMuted with: 'Free Software never looked so fit.'.
77 Added: html form: [
78 Added: html formButton
79 Added: bePrimary;
80 Added: beLarge;
81 Added: class: 'mt-3';
82 Added: callback: [
83 Added: self session pageManager activePage:
84 Added: (self session pageManager mainPages at: 2) ];
85 Added: with: 'Log a workout' ] ]
86 Added: ]
87 Added:
88 Added: { #category : #rendering }
89 Added: MFTCard >> renderMainUserCardOn: html [
90 Added:
91 Added: | lastLoginDate subtitle |
92 Added: lastLoginDate := DateAndTime fromString:
93 Added: self session user lastLoginDateAndTime.
94 Added: subtitle := {
95 Added: 'Last logged in'.
96 Added: (lastLoginDate dayOfWeekName , ',').
97 Added: lastLoginDate asDate asString.
98 Added: 'at'.
99 Added: lastLoginDate asTimeUTC asString.
100 Added: 'UTC' } joinUsing: ' '.
101 Added: html card: [
102 Added: html jumbotron
103 Added: class: 'p-3 bg-light';
104 Added: with: [
105 Added: html displayHeading
106 Added: level: 1;
107 Added: with: [
108 Added: html
109 Added: text: 'Welcome, ';
110 Added: code: self session user username;
111 Added: text: '!' ].
112 Added: html big textMuted with: subtitle ] ]
113 Added: ]
114 Added:
115 Added: { #category : #rendering }
116 Added: MFTCard >> renderPromisesCardOn: html [
117 Added:
118 Added: html cardBody: [
119 Added: html unorderedList: [
120 Added: html listItem:
121 Added: 'Every single feature shall always be free of charge.'.
122 Added: html listItem:
123 Added: 'No user data shall ever be sold, under any circumstances.'.
124 Added: html listItem: 'No user data shall ever be tracked.' ] ]
125 Added: ]
126 Added:
127 Added: { #category : #rendering }
128 Added: MFTCard >> renderWeightsCardOn: html [
129 Added:
130 Added: html cardBody: [
131 Added: html paragraph: 'Great job logging your weight '
132 Added: , self session user weights size asString , ' times!'.
133 Added: html form
134 Added: class: 'my-3';
135 Added: with: [
136 Added: html formButton
137 Added: bePrimary;
138 Added: beSmall;
139 Added: callback: [
140 Added: self session pageManager activePage:
141 Added: (self session pageManager mainPages at: 3) ];
142 Added: with: 'Log a weight' ].
143 Added: self renderWeightsGraphOn: html ]
144 Added: ]
145 Added:
146 Added: { #category : #rendering }
147 Added: MFTCard >> renderWeightsGraphOn: html [
148 Added:
149 Added: | labels data |
150 Added: labels := self session user weights collect: [ :weight |
151 Added: weight date yyyymmdd ].
152 Added: data := self session user weights collect: #value.
153 Added: html canvas
154 Added: id: 'weightChart';
155 Added: width: 4;
156 Added: height: 1.
157 Added: html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
158 Added: html script:
159 Added: 'var ctx = document.getElementById("weightChart").getContext("2d");
160 Added: var weightChart = new Chart(ctx, {
161 Added: type: "line",
162 Added: data: {
163 Added: labels: ' , labels asJavascript , ',
164 Added: datasets: [{
165 Added: label: "weight",
166 Added: data: ' , data asJavascript , ',
167 Added: backgroundColor: "rgba(255, 99, 132, 0.2)",
168 Added: borderColor: "rgba(255, 99, 132, 1)",
169 Added: borderWidth: 1
170 Added: }]
171 Added: },
172 Added: options: {
173 Added: scales: {
174 Added: yAxes: [{
175 Added: ticks: {
176 Added: beginAtZero: true
177 Added: }
178 Added: }]
179 Added: }
180 Added: }
181 Added: });'
182 Added: ]
183 Added:
184 Added: { #category : #rendering }
185 Added: MFTCard >> renderWorkoutsCardOn: html [
186 Added:
187 Added: html cardBody: [
188 Added: html paragraph:
189 Added: 'Great job logging ' , self session user workouts size asString
190 Added: , ' workouts!'.
191 Added: html form
192 Added: class: 'my-3';
193 Added: with: [
194 Added: html formButton
195 Added: bePrimary;
196 Added: beSmall;
197 Added: callback: [
198 Added: self session pageManager activePage:
199 Added: (self session pageManager mainPages at: 2) ];
200 Added: with: 'Log a workout' ].
201 Added: self renderWorkoutsGraphOn: html ]
202 Added: ]
203 Added:
204 Added: { #category : #rendering }
205 Added: MFTCard >> renderWorkoutsGraphOn: html [
206 Added:
207 Added: | exercises labels data repTally |
208 Added: exercises := self session user workouts flatCollect: #exercises.
209 Added: repTally := Dictionary new.
210 Added: exercises do: [ :exercise |
211 Added: repTally at: exercise englishName ifAbsentPut: [ 0 ].
212 Added: repTally
213 Added: at: exercise englishName
214 Added: put: (repTally at: exercise englishName) + exercise repsBySet sum ].
215 Added: labels := repTally keys.
216 Added: data := repTally values.
217 Added: html div
218 Added: style: 'max-width: 24rem';
219 Added: with: [
220 Added: html canvas
221 Added: id: 'workoutChart';
222 Added: width: 4;
223 Added: height: 1.
224 Added: html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
225 Added: html script:
226 Added: 'var ctx = document.getElementById("workoutChart").getContext("2d");
227 Added: var workoutChart = new Chart(ctx, {
228 Added: type: "doughnut",
229 Added: data: {
230 Added: labels: ' , labels asJavascript , ',
231 Added: datasets: [{
232 Added: data: ' , data asJavascript , '
233 Added: }]
234 Added: },
235 Added: options: {
236 Added: title: {
237 Added: display: true,
238 Added: text: "Reps by exercise"
239 Added: }
240 Added: }
241 Added: });' ]
242 Added: ]
243 Added:
244 Added: { #category : #accessing }
245 Added: MFTCard >> title [
246 Added:
247 Added: ^ title = self defaultTitle
248 Added: ifTrue: [ ^ type asCapitalizedPhrase ]
249 Added: ifFalse: [ ^ title ]
250 Added: ]
251 Added:
252 Added: { #category : #accessing }
253 Added: MFTCard >> title: aString [
254 Added:
255 Added: title := aString
256 Added: ]
257 Added:
258 Added: { #category : #accessing }
259 Added: MFTCard >> type [
260 Added:
261 Added: ^ type
262 Added: ]
263 Added:
264 Added: { #category : #accessing }
265 Added: MFTCard >> type: anObject [
266 Added:
267 Added: type := anObject
268 Added: ]
src/MyFitnessTracker/MFTExercise.class.st
index 63e8b33a..5d766485 100644..100644
@@ -237,7 +237,9 @@
237 237 { #category : #'as yet unclassified' }
238 238 MFTExercise >> repsBySet [
239 239
240 Removed: ^ sets collect: #reps
240 Added: ^ (sets collect: #reps)
241 Added: ifNotEmpty: [ ^ sets collect: #reps ]
242 Added: ifEmpty: [ ^ { 0 } ]
241 243 ]
242 244
243 245 { #category : #accessing }
src/MyFitnessTracker/MFTFooterComponent.class.st
index f358db2e..cd991772 100644..100644
@@ -12,5 +12,5 @@
12 12 with: [
13 13 html card: [
14 14 html cardHeader:
15 Removed: 'Copyright ' , Date today year asString , ' Marius PETER' ] ]
15 Added: 'Copyright 2023-' , Date today year asString , ' Marius PETER' ] ]
16 16 ]
src/MyFitnessTracker/MFTPublicLandingPage.class.st
index ccf2c59a..094858b1 100644..100644
@@ -15,134 +15,9 @@
15 15 MFTPublicLandingPage >> renderContentOn: html [
16 16
17 17 html container: [
18 Removed: self renderMainCardOn: html.
19 Removed: self renderWorkoutsGraphChartOn: html.
20 Removed: self renderWeightsGraphChartOn: html.
21 Removed: self renderPromisesCardOn: html ]
22 Removed: ]
23 Removed:
24 Removed: { #category : #rendering }
25 Removed: MFTPublicLandingPage >> renderMainCardOn: html [
26 Removed:
27 Removed: html card: [
28 Removed: html jumbotron
29 Removed: class: 'p-3 bg-light';
30 Removed: with: [
31 Removed: html displayHeading
32 Removed: level: 1;
33 Removed: with: 'Welcome to My Fitness Tracker!'.
34 Removed: html big textMuted with: 'Free Software never looked so fit.'.
35 Removed: html form: [ "TODO"
36 Removed: html formButton
37 Removed: bePrimary;
38 Removed: beLarge;
39 Removed: class: 'mt-3';
40 Removed: callback: [
41 Removed: self session pageManager activePage:
42 Removed: (self session pageManager mainPages at: 2) ];
43 Removed: with: 'Log a workout' ] ] ]
44 Removed: ]
45 Removed:
46 Removed: { #category : #rendering }
47 Removed: MFTPublicLandingPage >> renderPromisesCardOn: html [
48 Removed:
49 Removed: html card
50 Removed: marginTopAndBottom: 3;
51 Removed: with: [
52 Removed: html cardHeader: 'My promise to all account owners...'.
53 Removed: html cardBody: [
54 Removed: html unorderedList: [
55 Removed: html listItem: 'All features shall always be entirely free.'.
56 Removed: html listItem: 'No user data shall ever be sold.'.
57 Removed: html listItem: '.' ] ] ]
58 Removed: ]
59 Removed:
60 Removed: { #category : #rendering }
61 Removed: MFTPublicLandingPage >> renderWeightsGraphChartOn: html [
62 Removed:
63 Removed: html card with: [
64 Removed: html cardHeader: 'Weight'.
65 Removed: html cardBody: [ self renderWeightsGraphOn: html ] ]
66 Removed: ]
67 Removed:
68 Removed: { #category : #rendering }
69 Removed: MFTPublicLandingPage >> renderWeightsGraphOn: html [
70 Removed:
71 Removed: | labels data |
72 Removed: labels := self session weights collect: #date.
73 Removed: data := #( 10 15 40 25 ).
74 Removed: html canvas
75 Removed: id: 'weightChart';
76 Removed: width: 100;
77 Removed: height: 30.
78 Removed: html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
79 Removed: html script:
80 Removed: 'var ctx = document.getElementById("weightChart").getContext("2d");
81 Removed: var weightChart = new Chart(ctx, {
82 Removed: type: "bar",
83 Removed: data: {
84 Removed: labels: ' , labels asJavascript , ',
85 Removed: datasets: [{
86 Removed: label: "# of Votes",
87 Removed: data: ' , data asJavascript , ',
88 Removed: backgroundColor: "rgba(255, 99, 132, 0.2)",
89 Removed: borderColor: "rgba(255, 99, 132, 1)",
90 Removed: borderWidth: 1
91 Removed: }]
92 Removed: },
93 Removed: options: {
94 Removed: scales: {
95 Removed: yAxes: [{
96 Removed: ticks: {
97 Removed: beginAtZero: true
98 Removed: }
99 Removed: }]
100 Removed: }
101 Removed: }
102 Removed: });'
103 Removed: ]
104 Removed:
105 Removed: { #category : #rendering }
106 Removed: MFTPublicLandingPage >> renderWorkoutsGraphChartOn: html [
107 Removed:
108 Removed: html card with: [
109 Removed: html cardHeader: 'Workouts'.
110 Removed: html cardBody: [ self renderWorkoutsGraphOn: html ] ]
111 Removed: ]
112 Removed:
113 Removed: { #category : #rendering }
114 Removed: MFTPublicLandingPage >> renderWorkoutsGraphOn: html [
115 Removed:
116 Removed: | labels data |
117 Removed: labels := #( 'January' 'February' 'March' 'April' ).
118 Removed: data := #( 10 15 40 25 ).
119 Removed: html canvas
120 Removed: id: 'workoutChart';
121 Removed: width: 100;
122 Removed: height: 30.
123 Removed: html script url: 'https://cdn.jsdelivr.net/npm/chart.js'.
124 Removed: html script:
125 Removed: 'var ctx = document.getElementById("workoutChart").getContext("2d");
126 Removed: var workoutChart = new Chart(ctx, {
127 Removed: type: "bar",
128 Removed: data: {
129 Removed: labels: ' , labels asJavascript , ',
130 Removed: datasets: [{
131 Removed: label: "# of Votes",
132 Removed: data: ' , data asJavascript , ',
133 Removed: backgroundColor: "rgba(255, 99, 132, 0.2)",
134 Removed: borderColor: "rgba(255, 99, 132, 1)",
135 Removed: borderWidth: 1
136 Removed: }]
137 Removed: },
138 Removed: options: {
139 Removed: scales: {
140 Removed: yAxes: [{
141 Removed: ticks: {
142 Removed: beginAtZero: true
143 Removed: }
144 Removed: }]
145 Removed: }
146 Removed: }
147 Removed: });'
18 Added: html render: (MFTCard type: #mainPublic).
19 Added: html render: (MFTCard type: #workouts).
20 Added: html render: (MFTCard type: #weights).
21 Added: html render:
22 Added: (MFTCard type: #promises title: 'My promises to all users') ]
148 23 ]
src/MyFitnessTracker/MFTSession.class.st
index 1b358065..242b9370 100644..100644
@@ -25,8 +25,8 @@
25 25 MFTSession >> initialize [
26 26
27 27 super initialize.
28 Removed: user := MFTUser new.
29 Removed: pageManager := MFTPageManager new.
28 Added: user := MFTUser newDefault.
29 Added: pageManager := MFTPageManager new
30 30 ]
31 31
32 32 { #category : #login }
src/MyFitnessTracker/MFTUser.class.st
index 53650a16..86c5931c 100644..100644
@@ -8,7 +8,9 @@
8 8 'realFirstName',
9 9 'realLastName',
10 10 'isLoggedIn',
11 Removed: 'lastLoginDateAndTime'
11 Added: 'lastLoginDateAndTime',
12 Added: 'workouts',
13 Added: 'weights'
12 14 ],
13 15 #category : #'MyFitnessTracker-Model'
14 16 }
@@ -26,6 +28,19 @@
26 28 ]
27 29
28 30 { #category : #'instance creation' }
31 Added: MFTUser class >> newDefault [
32 Added:
33 Added: ^ self new
34 Added: username: 'mrs_default';
35 Added: realFirstName: 'Deborah';
36 Added: realLastName: 'Default';
37 Added: dateOfBirth: Date today;
38 Added: isLoggedIn: false;
39 Added: workouts: MFTWorkoutsListPage placeholder workouts;
40 Added: weights: MFTWeightsListPage placeholder weights
41 Added: ]
42 Added:
43 Added: { #category : #'instance creation' }
29 44 MFTUser class >> newFromDictionary: aDictionary [
30 45
31 46 ^ self new
@@ -67,8 +82,9 @@
67 82 realFirstName := String new.
68 83 realLastName := String new.
69 84 dateOfBirth := Date new.
70 Removed:
71 Removed: isLoggedIn := false
85 Added: isLoggedIn := false.
86 Added: workouts := OrderedCollection new.
87 Added: weights := OrderedCollection new
72 88 ]
73 89
74 90 { #category : #accessing }
@@ -147,4 +163,28 @@
147 163 MFTUser >> username: anObject [
148 164
149 165 username := anObject
166 Added: ]
167 Added:
168 Added: { #category : #accessing }
169 Added: MFTUser >> weights [
170 Added:
171 Added: ^ weights
172 Added: ]
173 Added:
174 Added: { #category : #accessing }
175 Added: MFTUser >> weights: anObject [
176 Added:
177 Added: weights := anObject
178 Added: ]
179 Added:
180 Added: { #category : #accessing }
181 Added: MFTUser >> workouts [
182 Added:
183 Added: ^ workouts
184 Added: ]
185 Added:
186 Added: { #category : #accessing }
187 Added: MFTUser >> workouts: anObject [
188 Added:
189 Added: workouts := anObject
150 190 ]
src/MyFitnessTracker/MFTUserLandingPage.class.st
index 05ec5a96..46d9fab6 100644..100644
@@ -15,26 +15,9 @@
15 15 MFTUserLandingPage >> renderContentOn: html [
16 16
17 17 html container: [
18 Removed: self renderMainCardOn: html.
19 Removed: self renderWorkoutsSummaryCardOn: html ]
20 Removed: ]
21 Removed:
22 Removed: { #category : #rendering }
23 Removed: MFTUserLandingPage >> renderMainCardOn: html [
24 Removed:
25 Removed: html card: [
26 Removed: html jumbotron
27 Removed: class: 'p-3 bg-light';
28 Removed: with: [
29 Removed: html displayHeading
30 Removed: level: 1;
31 Removed: with: [
32 Removed: html
33 Removed: text: 'Welcome, ';
34 Removed: code: self session user username;
35 Removed: text: '!' ].
36 Removed: html big textMuted with:
37 Removed: 'Last logged in ' , self session user lastLoginDateAndTime ] ]
18 Added: html render: (MFTCard type: #mainUser).
19 Added: html render: (MFTCard type: #workouts).
20 Added: html render: MFTCard new ]
38 21 ]
39 22
40 23 { #category : #rendering }
src/MyFitnessTracker/MFTWeightsListPage.class.st
index 5424777d..b42e94fb 100644..100644
@@ -24,7 +24,7 @@
24 24
25 25 | newWeight |
26 26 newWeight := self call: MFTWeightDialog new.
27 Removed: newWeight ifNotNil: [ weights addFirst: newWeight ]
27 Added: newWeight ifNotNil: [ self session user weights addLast: newWeight ]
28 28 ]
29 29
30 30 { #category : #accessing }
@@ -125,7 +125,13 @@
125 125 ]
126 126
127 127 { #category : #accessing }
128 Removed: MFTWeightsListPage >> weights: aWeight [
128 Added: MFTWeightsListPage >> weights [
129 129
130 Removed: weights := aWeight
130 Added: ^ weights
131 Added: ]
132 Added:
133 Added: { #category : #accessing }
134 Added: MFTWeightsListPage >> weights: aMFTWeightCollection [
135 Added:
136 Added: weights := aMFTWeightCollection
131 137 ]
src/MyFitnessTracker/MFTWorkoutsListPage.class.st
index d315fb21..105b4e64 100644..100644
@@ -184,11 +184,14 @@
184 184 html accordionButton
185 185 dataToggle: 'collapse';
186 186 dataTarget: '#collapse' , i asString;
187 Removed: with:
188 Removed: workout date yyyymmdd , (workout primaryMuscle englishName
189 Removed: ifNotEmpty: [
190 Removed: ' | ' , workout primaryMuscle englishName asString ]
191 Removed: ifEmpty: [ '' ]) ].
187 Added: with: [
188 Added: html
189 Added: text:
190 Added: workout date yyyymmdd , ' ('
191 Added: , workout date dayOfWeekName asString , ') ';
192 Added: space.
193 Added: workout primaryMuscle englishName ifNotEmpty: [
194 Added: html strong: workout primaryMuscle englishName asString ] ] ].
192 195 html accordionCollapse
193 196 id: 'collapse' , i asString;
194 197 class: 'collapse';
@@ -206,7 +209,13 @@
206 209 ]
207 210
208 211 { #category : #accessing }
209 Removed: MFTWorkoutsListPage >> workouts: aListOfWorkouts [
212 Added: MFTWorkoutsListPage >> workouts [
210 213
211 Removed: workouts := aListOfWorkouts
214 Added: ^ workouts
215 Added: ]
216 Added:
217 Added: { #category : #accessing }
218 Added: MFTWorkoutsListPage >> workouts: aMFTWorkoutCollection [
219 Added:
220 Added: workouts := aMFTWorkoutCollection
212 221 ]