Skip to content

Commit

Permalink
Add other types of workouts without location
Browse files Browse the repository at this point in the history
Eg. add weight and repetition based workouts.

Signed-off-by: Jo Vandeginste <[email protected]>
  • Loading branch information
jovandeginste committed Jun 16, 2024
1 parent 11a7bc6 commit ef9d8de
Show file tree
Hide file tree
Showing 20 changed files with 441 additions and 90 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ build-dist: clean-dist
cp -R ./node_modules/@fortawesome/fontawesome-free/ ./assets/dist/fontawesome/
cp -v ./node_modules/apexcharts/dist/apexcharts.min.js ./assets/dist/
cp -v ./node_modules/apexcharts/dist/apexcharts.css ./assets/dist/
cp -v ./node_modules/htmx.org/dist/htmx.min.js ./assets/dist/


watch-tw:
Expand Down
16 changes: 16 additions & 0 deletions assets/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,18 @@ button[type="submit"]:hover,
}
}

input:invalid {
--tw-bg-opacity: 1;
background-color: rgb(252 165 165 / var(--tw-bg-opacity));
}

@media (prefers-color-scheme: dark) {
input:invalid {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
}

button[type="submit"] {
font-weight: 700;
}
Expand Down Expand Up @@ -2824,6 +2836,10 @@ table {
content: "\f00d";
}

.\[a-zA-Z\:\\-\\\.\] {
a-z-a--z: \-\.;
}

@media (min-width: 640px) {
.sm\:mt-0 {
margin-top: 0px;
Expand Down
4 changes: 4 additions & 0 deletions main.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
@apply bg-zinc-100 border-zinc-300 hover:border-amber-500 placeholder:text-slate-500;
@apply dark:bg-zinc-900 dark:border-zinc-700 dark:hover:border-amber-500 dark:placeholder:text-slate-500;
}
input:invalid {
@apply bg-red-300;
@apply dark:bg-red-800;
}
button[type="submit"] {
@apply font-bold;
}
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"@fortawesome/fontawesome-free": "^6.5.1",
"apexcharts": "^3.48.0",
"fullcalendar": "^6.1.11",
"htmx.org": "^1.9.12",
"leaflet": "^1.9.4",
"shareon": "^2.5.0",
"sorttable": "^1.0.2",
Expand Down
1 change: 1 addition & 0 deletions pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func (a *App) Configure() error {
if err := a.ConfigureDatabase(); err != nil {
return err
}

if err := a.ConfigureGeocoder(); err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/app/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func (a *App) secureRoutes(e *echo.Group) *echo.Group {
workoutsGroup.POST("/:id/delete", a.workoutsDeleteHandler).Name = "workout-delete"
workoutsGroup.POST("/:id/refresh", a.workoutsRefreshHandler).Name = "workout-refresh"
workoutsGroup.GET("/add", a.workoutsAddHandler).Name = "workout-add"
workoutsGroup.GET("/form", a.workoutsFormHandler).Name = "workout-form"

equipmentGroup := secureGroup.Group("/equipment")
equipmentGroup.GET("", a.equipmentHandler).Name = "equipment"
Expand Down
69 changes: 69 additions & 0 deletions pkg/app/workouts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ import (
"mime/multipart"
"net/http"
"strings"
"time"

"github.com/jovandeginste/workout-tracker/pkg/database"
"github.com/labstack/echo/v4"
)

const (
htmlDateFormat = "2006-01-02T15:04"
htmlDurationFormat = "15:04"
)

func uploadedFile(file *multipart.FileHeader) ([]byte, error) {
src, err := file.Open()
if err != nil {
Expand All @@ -26,7 +32,70 @@ func uploadedFile(file *multipart.FileHeader) ([]byte, error) {
return content, nil
}

type ManualWorkout struct {
Name string `form:"name"`
Date string `form:"date"`
Duration string `form:"duration"`
Repetitions int `form:"repetitions"`
Weight float64 `form:"weight"`
Notes string `form:"notes"`
Type database.WorkoutType `form:"type"`
}

func (m *ManualWorkout) ToDate() time.Time {
d, err := time.Parse(htmlDateFormat, m.Date)
if err != nil {
return time.Time{}
}

return d
}

func (m *ManualWorkout) ToDuration() time.Duration {
d, err := time.Parse(htmlDurationFormat, m.Duration)
if err != nil {
return 0
}

return time.Duration(d.Hour())*time.Hour + time.Duration(d.Minute())*time.Minute
}

func (a *App) addWorkout(c echo.Context) error {
if strings.HasPrefix(c.Request().Header.Get(echo.HeaderContentType), echo.MIMEMultipartForm) {
return a.addWorkoutFromFile(c)
}

d := &ManualWorkout{}

if err := c.Bind(d); err != nil {
return a.redirectWithError(c, "/workouts", err)
}

dDate := d.ToDate()
dDuration := d.ToDuration()

w := database.Workout{
Name: d.Name,
Notes: d.Notes,
Date: &dDate,
Type: d.Type,
User: a.getCurrentUser(c),
UserID: a.getCurrentUser(c).ID,
Data: &database.MapData{
TotalDuration: dDuration,
TotalRepetitions: d.Repetitions,
TotalWeight: d.Weight,
},
}

if err := w.Save(a.db); err != nil {
return a.redirectWithError(c, "/workouts", err)
}

return c.Redirect(http.StatusFound, a.echo.Reverse("workouts"))
}

func (a *App) addWorkoutFromFile(c echo.Context) error {
form, err := c.MultipartForm()
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions pkg/app/workouts_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ func (a *App) workoutsAddHandler(c echo.Context) error {
return c.Render(http.StatusOK, "workouts_add.html", data)
}

func (a *App) workoutsFormHandler(c echo.Context) error {
t := database.WorkoutType(c.FormValue("type"))
return c.Render(http.StatusOK, "workout_form.html", t)
}

func (a *App) workoutsDeleteHandler(c echo.Context) error { //nolint:dupl
workout, err := a.getWorkout(c)
if err != nil {
Expand Down
9 changes: 7 additions & 2 deletions pkg/database/equipment.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,17 @@ func (e *Equipment) GetTotals() (WorkoutTotals, error) {
if w.Type.IsDuration() {
rs.Duration += w.Duration()
}

if w.Type.IsRepetition() {
rs.Repetitions += w.Repetitions()
}
}

return rs, nil
}

type WorkoutTotals struct {
Distance float64
Duration time.Duration
Distance float64
Duration time.Duration
Repetitions int
}
10 changes: 10 additions & 0 deletions pkg/database/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type UserPreferredUnits struct {
SpeedRaw string `form:"speed" json:"speed"` // The user's preferred speed unit
DistanceRaw string `form:"distance" json:"distance"` // The user's preferred distance unit
ElevationRaw string `form:"elevation" json:"elevation"` // The user's preferred elevation unit
WeightRaw string `form:"weight" json:"weight"` // The user's preferred weight unit
}

func (u UserPreferredUnits) Tempo() string {
Expand All @@ -50,6 +51,15 @@ func (u UserPreferredUnits) Elevation() string {
}
}

func (u UserPreferredUnits) Weight() string {
switch u.WeightRaw {
case "lbs":
return "lbs"
default:
return "kg"
}
}

func (u UserPreferredUnits) Distance() string {
switch u.DistanceRaw {
case "mi":
Expand Down
103 changes: 86 additions & 17 deletions pkg/database/workout_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,109 @@ const (
// We need to add each of these types to the "messages.html" partial view.
// Then it gets picked up by the i18n system, added to the list of translatable
// strings, etc.
WorkoutTypeAutoDetect WorkoutType = "auto"
WorkoutTypeRunning WorkoutType = "running"
WorkoutTypeCycling WorkoutType = "cycling"
WorkoutTypeWalking WorkoutType = "walking"
WorkoutTypeSkiing WorkoutType = "skiing"
WorkoutTypeSnowboarding WorkoutType = "snowboarding"
WorkoutTypeSwimming WorkoutType = "swimming"
WorkoutTypeKayaking WorkoutType = "kayaking"
WorkoutTypeGolfing WorkoutType = "golfing"
WorkoutTypeHiking WorkoutType = "hiking"
WorkoutTypeAutoDetect WorkoutType = "auto"
WorkoutTypeRunning WorkoutType = "running"
WorkoutTypeCycling WorkoutType = "cycling"
WorkoutTypeWalking WorkoutType = "walking"
WorkoutTypeSkiing WorkoutType = "skiing"
WorkoutTypeSnowboarding WorkoutType = "snowboarding"
WorkoutTypeSwimming WorkoutType = "swimming"
WorkoutTypeKayaking WorkoutType = "kayaking"
WorkoutTypeGolfing WorkoutType = "golfing"
WorkoutTypeHiking WorkoutType = "hiking"
WorkoutTypePushups WorkoutType = "push-ups"
WorkoutTypeWeightLifting WorkoutType = "weight lifting"
)

func WorkoutTypes() []WorkoutType {
return []WorkoutType{WorkoutTypeRunning, WorkoutTypeCycling, WorkoutTypeWalking, WorkoutTypeSkiing, WorkoutTypeSnowboarding, WorkoutTypeSwimming, WorkoutTypeKayaking, WorkoutTypeGolfing, WorkoutTypeHiking}
type WorkoutTypeConfiguration struct {
Location bool
Distance bool
Repetition bool
Weight bool
}

var workoutTypeConfigs = map[WorkoutType]WorkoutTypeConfiguration{
WorkoutTypeRunning: {Location: true, Distance: true, Repetition: false, Weight: false},
WorkoutTypeCycling: {Location: true, Distance: true, Repetition: false, Weight: false},
WorkoutTypeWalking: {Location: true, Distance: true, Repetition: false, Weight: false},
WorkoutTypeSkiing: {Location: true, Distance: true, Repetition: false, Weight: false},
WorkoutTypeSnowboarding: {Location: true, Distance: true, Repetition: false, Weight: false},
WorkoutTypeSwimming: {Location: true, Distance: true, Repetition: false, Weight: false},
WorkoutTypeKayaking: {Location: true, Distance: true, Repetition: false, Weight: false},
WorkoutTypeGolfing: {Location: true, Distance: true, Repetition: false, Weight: false},
WorkoutTypeHiking: {Location: true, Distance: true, Repetition: false, Weight: false},

WorkoutTypePushups: {Location: false, Distance: false, Repetition: true, Weight: false},
WorkoutTypeWeightLifting: {Location: false, Distance: false, Repetition: true, Weight: true},
}

func DurationWorkoutTypes() []WorkoutType {
return []WorkoutType{WorkoutTypeRunning, WorkoutTypeCycling, WorkoutTypeWalking, WorkoutTypeSkiing, WorkoutTypeSnowboarding, WorkoutTypeSwimming, WorkoutTypeKayaking, WorkoutTypeGolfing, WorkoutTypeHiking}
func WorkoutTypes() []WorkoutType {
keys := []WorkoutType{}

for k := range workoutTypeConfigs {
keys = append(keys, k)
}

slices.Sort(keys)

return keys
}

func DistanceWorkoutTypes() []WorkoutType {
return []WorkoutType{WorkoutTypeRunning, WorkoutTypeCycling, WorkoutTypeWalking, WorkoutTypeSkiing, WorkoutTypeSnowboarding, WorkoutTypeSwimming, WorkoutTypeKayaking, WorkoutTypeGolfing, WorkoutTypeHiking}
keys := []WorkoutType{}

for k, c := range workoutTypeConfigs {
if !c.Distance {
continue
}

keys = append(keys, k)
}

slices.Sort(keys)

return keys
}

func LocationWorkoutTypes() []WorkoutType {
keys := []WorkoutType{}

for k, c := range workoutTypeConfigs {
if !c.Location {
continue
}

keys = append(keys, k)
}

slices.Sort(keys)

return keys
}

func (wt WorkoutType) String() string {
return string(wt)
}

func (wt WorkoutType) IsDistance() bool {
return slices.Contains(DistanceWorkoutTypes(), wt)
return workoutTypeConfigs[wt].Distance
}

func (wt WorkoutType) IsRepetition() bool {
return workoutTypeConfigs[wt].Repetition
}

func (wt WorkoutType) IsDuration() bool {
return slices.Contains(DurationWorkoutTypes(), wt)
_, ok := workoutTypeConfigs[wt]
return ok
}

func (wt WorkoutType) IsWeight() bool {
return workoutTypeConfigs[wt].Weight
}

func (wt WorkoutType) IsLocation() bool {
return workoutTypeConfigs[wt].Location
}

func AsWorkoutType(s string) WorkoutType {
Expand Down
8 changes: 8 additions & 0 deletions pkg/database/workouts.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ type GPXData struct {
Filename string // The filename of the file
}

func (w *Workout) Repetitions() int {
if w.Data == nil {
return 0
}

return w.Data.TotalRepetitions
}

func (w *Workout) Duration() time.Duration {
if w.Data == nil {
return 0
Expand Down
Loading

0 comments on commit ef9d8de

Please sign in to comment.