Skip to content

Commit

Permalink
Merge pull request #115 from jovandeginste/breakdown-responsive
Browse files Browse the repository at this point in the history
Improve responsive design of workout breakdown
  • Loading branch information
jovandeginste authored Apr 15, 2024
2 parents 3857e81 + b5eb486 commit 2f9e16a
Show file tree
Hide file tree
Showing 20 changed files with 82 additions and 55 deletions.
66 changes: 43 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
A workout tracking web application for personal use (or family, friends), geared towards running and other GPX-based
activities
A workout tracking web application for personal use (or family, friends), geared
towards running and other GPX-based activities

Self-hosted, everything included.

Expand All @@ -9,7 +9,8 @@ Heavily inspired by [FitTrackee](https://github.com/SamR1/FitTrackee) :heart:.

### Docker

Run the latest master image from GitHub Container Registry (master and release images are available for amd64 and arm64):
Run the latest master image from GitHub Container Registry (master and release
images are available for amd64 and arm64):

```bash
# Latest master build
Expand Down Expand Up @@ -51,7 +52,9 @@ docker compose up -d

### Natively

Download a [pre-built binary](https://github.com/jovandeginste/workout-tracker/releases) or build it yourself (see [Development](#development) below).
Download a [pre-built
binary](https://github.com/jovandeginste/workout-tracker/releases) or build it
yourself (see [Development](#development) below).

Eg. for v0.11.3 on Linux x86_64:

Expand All @@ -68,13 +71,15 @@ export WT_JWT_ENCRYPTION_KEY=my-secret-key
./workout-tracker
```

This will create a new database file in the current directory and start the web server at `http://localhost:8080`.
This will create a new database file in the current directory and start the web
server at `http://localhost:8080`.

## Screenshots

### Login page

![](docs/login.png)

Login / registration form

- new users have to be activated by an admin
Expand All @@ -83,6 +88,7 @@ Login / registration form
### Dashboard

![](docs/dashboard.png)

Dashboard view with:

- personal totals
Expand All @@ -93,17 +99,20 @@ Dashboard view with:
### Overview of workouts

![](docs/workout_overview.png)

Overview of all your activities, with summaries. The columns are sortable.

### Details of a single workout

![](docs/single_workout-dark.png)

Details of a workout, with:

- a zoomable, dragable map of the GPX track with more details per point
- many summarized statistics
- a breakdown per kilometer
- a breakdown per kilometer or per mile
- track color based on elevation of the segment
- graph of average speed and elevation per minute

### Tooltips for even more information

Expand All @@ -118,8 +127,10 @@ Details of a workout, with:
![](docs/upload_workouts.png)

- Upload one or multiple GPX files.
- Pick the type (running, cycling, ...) or let the application guess based on average speed
- The files are parsed when uploaded: statistics and other information are calculated and stored in the database (serialized).
- Pick the type (running, cycling, ...) or let the application guess based on
average speed
- The files are parsed when uploaded: statistics and other information are
calculated and stored in the database (serialized).

### Statistics to follow your progress

Expand Down Expand Up @@ -150,9 +161,11 @@ Details of a workout, with:

## Configuration

The web server looks for a file `workout-tracker.yaml` (or `json` or `toml`) in the current directory, or takes it's
configuration from environment variables. The most important variable is the JWT encryption key. If you don't provide
it, the key is randomly generated every time the server starts, invalidating all current sessions.
The web server looks for a file `workout-tracker.yaml` (or `json` or `toml`) in
the current directory, or takes it's configuration from environment variables.
The most important variable is the JWT encryption key. If you don't provide it,
the key is randomly generated every time the server starts, invalidating all
current sessions.

Generate a secure key and write it to `workout-tracker.yaml`:

Expand Down Expand Up @@ -180,10 +193,12 @@ WT_REGISTRATION_DISABLED="false"
WT_SOCIALS_DISABLED="false"
```

After starting the server, you can access it at <http://localhost:8080> (the default port). A login form is shown.
After starting the server, you can access it at <http://localhost:8080> (the
default port). A login form is shown.

If no users are in the database (eg. when starting with an empty database), a default `admin` user is created with
password `admin`. You should change this password in a production environment.
If no users are in the database (eg. when starting with an empty database), a
default `admin` user is created with password `admin`. You should change this
password in a production environment.

## Development

Expand All @@ -197,7 +212,8 @@ go build ./
./workout-tracker
```

This does not require npm or Tailwind, since the compiled css is included in the repository.
This does not require npm or Tailwind, since the compiled css is included in the
repository.

### Do some development

Expand Down Expand Up @@ -235,10 +251,12 @@ make clean # Removes build artifacts

A single binary that runs on any platform, with no dependencies.

The binary contains all assets to serve a web interface, through which you can upload your GPX files, visualize
your tracks and see their statistics and graphs. The web application is multi-user, with a simple registration and
authentication form, session cookies and JWT tokens). New accounts are inactive by default. An admin user can activate
(or edit, delete) accounts. The default database storage is a single SQLite file.
The binary contains all assets to serve a web interface, through which you can
upload your GPX files, visualize your tracks and see their statistics and
graphs. The web application is multi-user, with a simple registration and
authentication form, session cookies and JWT tokens). New accounts are inactive
by default. An admin user can activate (or edit, delete) accounts. The default
database storage is a single SQLite file.

## What technologies are used

Expand All @@ -256,12 +274,13 @@ authentication form, session cookies and JWT tokens). New accounts are inactive
- [Chart.js](https://cdn.jsdelivr.net/npm/chart.js)
- Docker

The application uses OpenStreetMap as its map provider and for geocoding a GPS coordinate to a location.
The application uses OpenStreetMap as its map provider and for geocoding a GPS
coordinate to a location.

## Compatiblity

This is a work in progress. If you find any problems, please let us know. The application is tested with GPX files from
these sources:
This is a work in progress. If you find any problems, please let us know. The
application is tested with GPX files from these sources:

- Garmin Connect (export to GPX)
- FitoTrack (automatic export to GPX)
Expand All @@ -276,7 +295,8 @@ these sources:
- added support for MySQL, but untested so far
- added support for Postgres by @icewind1991
- add support for other types of import files (eg. Garmin fit files)
- importing fit files works, kinda: there seems to be an issue with the elevation
- importing fit files works, kinda: there seems to be an issue with the
elevation
- see https://github.com/tormoder/fit/issues/87
- https://www.fitfileviewer.com/ gives the same elevation issue
- see if htmx is worth using
Expand Down
20 changes: 16 additions & 4 deletions assets/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -1819,6 +1819,10 @@ table {
height: 300px;
}

.h-\[400px\] {
height: 400px;
}

.min-h-\[450px\] {
min-height: 450px;
}
Expand All @@ -1835,10 +1839,6 @@ table {
flex-basis: 50%;
}

.basis-1\/3 {
flex-basis: 33.333333%;
}

.basis-1\/4 {
flex-basis: 25%;
}
Expand Down Expand Up @@ -1921,6 +1921,10 @@ table {
overflow: hidden;
}

.overflow-y-auto {
overflow-y: auto;
}

.overflow-y-hidden {
overflow-y: hidden;
}
Expand Down Expand Up @@ -2820,6 +2824,10 @@ table {
.xl\:hidden {
display: none;
}

.xl\:h-\[600px\] {
height: 600px;
}
}

@media (min-width: 1536px) {
Expand All @@ -2834,6 +2842,10 @@ table {
.\32xl\:hidden {
display: none;
}

.\32xl\:basis-1\/3 {
flex-basis: 33.333333%;
}
}

@media (prefers-color-scheme: dark) {
Expand Down
Binary file modified docs/dashboard-responsive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/login.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/responsive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/single_workout-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/single_workout-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/single_workout-responsive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/single_workout-theme.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/statistics-responsive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/statistics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/upload_workouts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/workout_overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion screenshots.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default async function () {

// Create screenshots for responsive view
page.setViewportSize({
width: 400,
width: 600,
height: 2000,
});
page.emulateMedia({
Expand Down
2 changes: 0 additions & 2 deletions translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
"I completed a workout: %s.": "Ich habe die Trainingseinheit %s absolviert.",
"It took me %s to go %s. I averaged %s.": "Es hat mich %s gekostet, um %s zu gehen. Ich habe im Durchschnitt %s.",
"Language": "Sprache",
"Lap": "Lap",
"Last distance": "Letzte Distanz",
"Leave blank to keep current password": "Leer lassen, um das aktuelle Passwort beizubehalten.",
"Location": "Standort",
"Logout": "Abmelden",
Expand Down
2 changes: 0 additions & 2 deletions translations/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
"I completed a workout: %s.": "I completed a workout: %s.",
"It took me %s to go %s. I averaged %s.": "It took me %s to go %s. I averaged %s.",
"Language": "Language",
"Lap": "Lap",
"Last distance": "Last distance",
"Leave blank to keep current password": "Leave blank to keep current password",
"Location": "Location",
"Logout": "Logout",
Expand Down
2 changes: 0 additions & 2 deletions translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
"I completed a workout: %s.": "Ik heb een workout voltooid: %s.",
"It took me %s to go %s. I averaged %s.": "Het duurde %s om %s te bereiken. Ik haalde gemiddeld %s.",
"Language": "Taal",
"Lap": "Ronde",
"Last distance": "Laatste afstand",
"Leave blank to keep current password": "Laat leeg om het huidige wachtwoord te behouden",
"Location": "Locatie",
"Logout": "Afmelden",
Expand Down
27 changes: 12 additions & 15 deletions views/partials/workout_breakdown.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
<thead>
<tr>
<th></th>
<th>{{ i18n "Lap" }}</th>
<th>{{ i18n "Distance" }}</th>
<th>{{ i18n "Duration" }}</th>
<th class="lg:hidden 3xl:table-cell">{{ i18n "Duration" }}</th>
<th>{{ i18n "Speed" }}</th>
<th>{{ i18n "Tempo" }}</th>
</tr>
</thead>
<tbody>
<tbody class="whitespace-nowrap font-mono">
{{ range .Items }}
<tr
{{
Expand All @@ -23,24 +22,22 @@
}}
onmouseout="clear_marker()"
>
<td>
{{ if .IsWorst }}
<span class="text-orange-600 {{ IconFor `worst` }}"></span>{{ end }} {{
if .IsBest }}
<span class="text-green-500 {{ IconFor `best` }}"></span>{{ end }}
<td class="text-right">
{{- if .IsWorst -}}
<span class="text-orange-600 {{ IconFor `worst` }}"></span>
{{- end -}} {{- if .IsBest -}}
<span class="text-green-500 {{ IconFor `best` }}"></span>
{{- end -}} {{ .Counter }}
</td>
<td class="text-center">{{ .Counter }}</td>
<td>
{{ .TotalDistance | HumanDistance }} {{
CurrentUser.PreferredUnits.Distance }}
</td>
<td>{{ .Duration | HumanDuration }}</td>
<td class="whitespace-nowrap font-mono">
{{ .Speed | HumanSpeed }} {{ CurrentUser.PreferredUnits.Speed }}
</td>
<td class="whitespace-nowrap font-mono">
{{ .Speed | HumanTempo }} {{ CurrentUser.PreferredUnits.Tempo }}
<td class="lg:hidden 3xl:table-cell">
{{ .TotalDuration | HumanDuration }}
</td>
<td>{{ .Speed | HumanSpeed }} {{ CurrentUser.PreferredUnits.Speed }}</td>
<td>{{ .Speed | HumanTempo }} {{ CurrentUser.PreferredUnits.Tempo }}</td>
</tr>
{{ end }}
</tbody>
Expand Down
16 changes: 10 additions & 6 deletions views/workouts/workouts_show.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ <h2 class="{{ IconFor .Type.String }}">
{{ .Name }} {{ with .Filename }}({{ . }}){{ end }}
</h2>
</div>
<div class="sm:flex sm:flex-wrap print:block">
<div class="basis-1/3 pagebreak">
<div class="lg:flex lg:flex-wrap print:block">
<div class="basis-1/2 2xl:basis-1/3 pagebreak">
<div class="inner-form">
<div
id="map"
Expand All @@ -57,13 +57,17 @@ <h2 class="{{ IconFor .Type.String }}">
.}} {{ end }}
</div>
</div>
<div class="basis-1/3">
<div class="basis-1/2 2xl:basis-1/3">
<div class="inner-form">{{ template "workout_details" . }}</div>
</div>
<div class="basis-1/3">
<div class="basis-1/2 2xl:basis-1/3">
<div class="inner-form">
{{ template "workout_breakdown" (.StatisticsPer 1
CurrentUser.PreferredUnits.Distance) }}
<div
class="h-[400px] xl:h-[600px] print:w-full print:h-[600px] overflow-y-auto"
>
{{ template "workout_breakdown" (.StatisticsPer 1
CurrentUser.PreferredUnits.Distance) }}
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit 2f9e16a

Please sign in to comment.