refactor: change history screen to use a list instead of cards
This commit is contained in:
parent
059db8f2fb
commit
084ea252a2
3 changed files with 147 additions and 103 deletions
|
|
@ -122,3 +122,20 @@ func (s *DatabaseService) GetTrainingCount() (int, error) {
|
||||||
}
|
}
|
||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DatabaseService) DeleteTraining(id int64) error {
|
||||||
|
query := "DELETE FROM training WHERE id = ?;"
|
||||||
|
_, err := s.DB.Exec(query, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DatabaseService) UpdateTraining(session *TrainingSession) error {
|
||||||
|
dateStr := session.Date.Format(time.RFC3339)
|
||||||
|
query := `
|
||||||
|
UPDATE training
|
||||||
|
SET date = ?, sets = ?, weightLeft = ?, weightRight = ?, repsPerSet = ?, duration = ?, program = ?, blockDay = ?
|
||||||
|
WHERE id = ?;
|
||||||
|
`
|
||||||
|
_, err := s.DB.Exec(query, dateStr, session.Sets, session.WeightLeft, session.WeightRight, session.RepsPerSet, session.Duration, session.Program, session.BlockDay, session.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package ui
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
|
|
@ -17,33 +18,137 @@ func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.Canvas
|
||||||
var history []data.TrainingSession
|
var history []data.TrainingSession
|
||||||
|
|
||||||
placeholder := widget.NewLabel("Noch keine Trainingsdaten vorhanden.")
|
placeholder := widget.NewLabel("Noch keine Trainingsdaten vorhanden.")
|
||||||
|
|
||||||
|
var showDetailDialog func(session data.TrainingSession, id int)
|
||||||
|
var refreshData func()
|
||||||
|
|
||||||
list := widget.NewList(
|
list := widget.NewList(
|
||||||
func() int { return len(history) },
|
func() int { return len(history) },
|
||||||
func() fyne.CanvasObject {
|
func() fyne.CanvasObject {
|
||||||
stats := container.NewGridWithColumns(3,
|
return container.NewGridWithColumns(4,
|
||||||
container.NewVBox(widget.NewLabel("Sätze"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
widget.NewLabel(""),
|
||||||
container.NewVBox(widget.NewLabel("Dauer"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
widget.NewLabel(""),
|
||||||
container.NewVBox(widget.NewLabel("Reps/Satz"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
widget.NewLabel(""),
|
||||||
|
widget.NewLabel(""),
|
||||||
)
|
)
|
||||||
return widget.NewCard("", "", container.NewVBox(widget.NewLabel(""), widget.NewSeparator(), stats))
|
|
||||||
},
|
},
|
||||||
func(i widget.ListItemID, o fyne.CanvasObject) {
|
func(i widget.ListItemID, o fyne.CanvasObject) {
|
||||||
session := history[i]
|
session := history[i]
|
||||||
card := o.(*widget.Card)
|
row := o.(*fyne.Container)
|
||||||
card.SetTitle(session.Date.Format("02.01.2006 15:04"))
|
row.Objects[0].(*widget.Label).SetText(session.Date.Format("02.01.2006 15:04"))
|
||||||
|
row.Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", session.Sets))
|
||||||
content := card.Content.(*fyne.Container)
|
row.Objects[2].(*widget.Label).SetText(utils.FormatDuration(session.Duration))
|
||||||
programLabel := content.Objects[0].(*widget.Label)
|
row.Objects[3].(*widget.Label).SetText(fmt.Sprintf("%d", session.RepsPerSet))
|
||||||
statsGrid := content.Objects[2].(*fyne.Container)
|
|
||||||
|
|
||||||
programLabel.SetText(fmt.Sprintf("%s - Tag %d", session.Program, session.BlockDay))
|
|
||||||
statsGrid.Objects[0].(*fyne.Container).Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", session.Sets))
|
|
||||||
statsGrid.Objects[1].(*fyne.Container).Objects[1].(*widget.Label).SetText(utils.FormatDuration(session.Duration))
|
|
||||||
statsGrid.Objects[2].(*fyne.Container).Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", session.RepsPerSet))
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
refreshData := func() {
|
list.OnSelected = func(id widget.ListItemID) {
|
||||||
|
session := history[id]
|
||||||
|
showDetailDialog(session, id)
|
||||||
|
list.Unselect(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
showDetailDialog = func(session data.TrainingSession, id int) {
|
||||||
|
details := container.NewVBox(
|
||||||
|
widget.NewLabel(fmt.Sprintf("Datum: %s", session.Date.Format("02.01.2006 15:04"))),
|
||||||
|
widget.NewLabel(fmt.Sprintf("Programm: %s", session.Program)),
|
||||||
|
widget.NewLabel(fmt.Sprintf("Block-Tag: %d", session.BlockDay)),
|
||||||
|
widget.NewLabel(fmt.Sprintf("Sätze: %d", session.Sets)),
|
||||||
|
widget.NewLabel(fmt.Sprintf("Dauer: %s", utils.FormatDuration(session.Duration))),
|
||||||
|
widget.NewLabel(fmt.Sprintf("Reps/Satz: %d", session.RepsPerSet)),
|
||||||
|
widget.NewLabel(fmt.Sprintf("Gewicht links: %.1f kg", session.WeightLeft)),
|
||||||
|
widget.NewLabel(fmt.Sprintf("Gewicht rechts: %.1f kg", session.WeightRight)),
|
||||||
|
)
|
||||||
|
|
||||||
|
deleteBtn := widget.NewButtonWithIcon("Löschen", theme.DeleteIcon(), func() {
|
||||||
|
dialog.ShowConfirm("Eintrag löschen", "Möchtest du diesen Eintrag wirklich löschen?", func(ok bool) {
|
||||||
|
if ok {
|
||||||
|
err := db.DeleteTraining(session.ID)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, parent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
refreshData()
|
||||||
|
}
|
||||||
|
}, parent)
|
||||||
|
})
|
||||||
|
|
||||||
|
editBtn := widget.NewButtonWithIcon("Bearbeiten", theme.DocumentCreateIcon(), func() {
|
||||||
|
setsEntry := widget.NewEntry()
|
||||||
|
setsEntry.SetText(fmt.Sprintf("%d", session.Sets))
|
||||||
|
|
||||||
|
durationEntry := widget.NewEntry()
|
||||||
|
durationEntry.SetText(fmt.Sprintf("%d", session.Duration)) // Dauer in Sekunden
|
||||||
|
|
||||||
|
repsEntry := widget.NewEntry()
|
||||||
|
repsEntry.SetText(fmt.Sprintf("%d", session.RepsPerSet))
|
||||||
|
|
||||||
|
weightLeftEntry := widget.NewEntry()
|
||||||
|
weightLeftEntry.SetText(fmt.Sprintf("%.1f", session.WeightLeft))
|
||||||
|
|
||||||
|
weightRightEntry := widget.NewEntry()
|
||||||
|
weightRightEntry.SetText(fmt.Sprintf("%.1f", session.WeightRight))
|
||||||
|
|
||||||
|
form := &widget.Form{
|
||||||
|
Items: []*widget.FormItem{
|
||||||
|
{Text: "Sätze", Widget: setsEntry},
|
||||||
|
{Text: "Dauer (Sekunden)", Widget: durationEntry},
|
||||||
|
{Text: "Reps/Satz", Widget: repsEntry},
|
||||||
|
{Text: "Gewicht links (kg)", Widget: weightLeftEntry},
|
||||||
|
{Text: "Gewicht rechts (kg)", Widget: weightRightEntry},
|
||||||
|
},
|
||||||
|
OnSubmit: func() {
|
||||||
|
var (
|
||||||
|
sets, reps, duration int
|
||||||
|
weightLeft, weightRight float64
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if sets, err = strconv.Atoi(setsEntry.Text); err != nil {
|
||||||
|
dialog.ShowError(fmt.Errorf("Ungültige Sätze-Zahl"), parent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if duration, err = strconv.Atoi(durationEntry.Text); err != nil {
|
||||||
|
dialog.ShowError(fmt.Errorf("Ungültige Dauer"), parent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if reps, err = strconv.Atoi(repsEntry.Text); err != nil {
|
||||||
|
dialog.ShowError(fmt.Errorf("Ungültige Wiederholungszahl"), parent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if weightLeft, err = strconv.ParseFloat(weightLeftEntry.Text, 64); err != nil {
|
||||||
|
dialog.ShowError(fmt.Errorf("Ungültiges Gewicht links"), parent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if weightRight, err = strconv.ParseFloat(weightRightEntry.Text, 64); err != nil {
|
||||||
|
dialog.ShowError(fmt.Errorf("Ungültiges Gewicht rechts"), parent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Sets = int64(sets)
|
||||||
|
session.Duration = int64(duration)
|
||||||
|
session.RepsPerSet = int64(reps)
|
||||||
|
session.WeightLeft = weightLeft
|
||||||
|
session.WeightRight = weightRight
|
||||||
|
|
||||||
|
if err := db.UpdateTraining(&session); err != nil {
|
||||||
|
dialog.ShowError(err, parent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialog.ShowInformation("Erfolg", "Trainingseintrag aktualisiert.", parent)
|
||||||
|
refreshData()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.ShowForm("Training bearbeiten", "Speichern", "Abbrechen", form.Items, func(ok bool) {
|
||||||
|
}, parent)
|
||||||
|
})
|
||||||
|
|
||||||
|
btns := container.NewHBox(editBtn, deleteBtn)
|
||||||
|
|
||||||
|
dialog.ShowCustom("Training Details", "Schließen", container.NewVBox(details, btns), parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshData = func() {
|
||||||
var err error
|
var err error
|
||||||
history, err = db.GetHistory()
|
history, err = db.GetHistory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -61,8 +166,15 @@ func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.Canvas
|
||||||
list.Refresh()
|
list.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header := container.NewGridWithColumns(4,
|
||||||
|
widget.NewLabelWithStyle("Datum", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
|
||||||
|
widget.NewLabelWithStyle("Sätze", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
|
||||||
|
widget.NewLabelWithStyle("Dauer", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
|
||||||
|
widget.NewLabelWithStyle("Reps/Satz", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
|
||||||
|
)
|
||||||
|
|
||||||
toolbar := widget.NewToolbar(widget.NewToolbarAction(theme.ViewRefreshIcon(), refreshData))
|
toolbar := widget.NewToolbar(widget.NewToolbarAction(theme.ViewRefreshIcon(), refreshData))
|
||||||
content := container.NewStack(list, container.NewCenter(placeholder))
|
content := container.NewVBox(header, container.NewStack(list, container.NewCenter(placeholder)))
|
||||||
layout := container.NewBorder(toolbar, nil, nil, nil, content)
|
layout := container.NewBorder(toolbar, nil, nil, nil, content)
|
||||||
|
|
||||||
if layout.Visible() {
|
if layout.Visible() {
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,3 @@
|
||||||
// package theme
|
|
||||||
//
|
|
||||||
// import (
|
|
||||||
//
|
|
||||||
// "image/color"
|
|
||||||
//
|
|
||||||
// "fyne.io/fyne/v2"
|
|
||||||
// "fyne.io/fyne/v2/theme"
|
|
||||||
//
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// // Definiere unsere benutzerdefinierten Farben basierend auf dem Mock-up
|
|
||||||
// var (
|
|
||||||
//
|
|
||||||
// ColorSlate900 = color.NRGBA{R: 0x0f, G: 0x17, B: 0x2a, A: 0xff} // bg-slate-900
|
|
||||||
// ColorSlate800 = color.NRGBA{R: 0x1e, G: 0x29, B: 0x3b, A: 0xff} // bg-slate-800
|
|
||||||
// ColorSlate700 = color.NRGBA{R: 0x33, G: 0x41, B: 0x55, A: 0xff} // bg-slate-700
|
|
||||||
// ColorSlate400 = color.NRGBA{R: 0x94, G: 0xa3, B: 0xb8, A: 0xff} // text-slate-400
|
|
||||||
// ColorSlate200 = color.NRGBA{R: 0xe2, G: 0xe8, B: 0xf0, A: 0xff} // text-slate-200
|
|
||||||
// ColorSky500 = color.NRGBA{R: 0x0e, G: 0xa5, B: 0xe9, A: 0xff} // bg-sky-500
|
|
||||||
// ColorSky400 = color.NRGBA{R: 0x38, G: 0xbd, B: 0xf8, A: 0xff} // text-sky-400
|
|
||||||
// ColorRed500 = color.NRGBA{R: 0xef, G: 0x44, B: 0x44, A: 0xff} // red-500
|
|
||||||
//
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// // KettlebellTheme ist unsere benutzerdefinierte Theme-Implementierung
|
|
||||||
// type KettlebellTheme struct{}
|
|
||||||
//
|
|
||||||
// func (t *KettlebellTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
|
|
||||||
// switch name {
|
|
||||||
// case theme.ColorNameBackground:
|
|
||||||
// return ColorSlate900
|
|
||||||
// case theme.ColorNameButton:
|
|
||||||
// return ColorSlate800
|
|
||||||
// case theme.ColorNameDisabledButton:
|
|
||||||
// return ColorSlate700
|
|
||||||
// case theme.ColorNamePrimary:
|
|
||||||
// return ColorSky500
|
|
||||||
// case theme.ColorNamePlaceHolder:
|
|
||||||
// return ColorSlate400
|
|
||||||
// case theme.ColorNameHover:
|
|
||||||
// return ColorSlate700
|
|
||||||
// case theme.ColorNameForeground:
|
|
||||||
// return ColorSlate200
|
|
||||||
// case theme.ColorNameDisabled:
|
|
||||||
// return ColorSlate400
|
|
||||||
// case theme.ColorNameError:
|
|
||||||
// return ColorRed500
|
|
||||||
// case theme.ColorNameInputBackground:
|
|
||||||
// return ColorSlate800
|
|
||||||
// case theme.ColorNameSeparator:
|
|
||||||
// return ColorSlate700
|
|
||||||
// default:
|
|
||||||
// return theme.DefaultTheme().Color(name, variant)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func (t *KettlebellTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
|
|
||||||
// return theme.DefaultTheme().Icon(name)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func (t *KettlebellTheme) Font(style fyne.TextStyle) fyne.Resource {
|
|
||||||
// return theme.DefaultTheme().Font(style)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func (t *KettlebellTheme) Size(name fyne.ThemeSizeName) float32 {
|
|
||||||
// switch name {
|
|
||||||
// case theme.SizeNamePadding:
|
|
||||||
// return 8
|
|
||||||
// case theme.SizeNameText:
|
|
||||||
// return 16
|
|
||||||
// case theme.SizeNameHeadingText:
|
|
||||||
// return 24
|
|
||||||
// case theme.SizeNameSubHeadingText:
|
|
||||||
// return 20
|
|
||||||
// default:
|
|
||||||
// return theme.DefaultTheme().Size(name)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
package theme
|
package theme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -86,21 +7,17 @@ import (
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// One Dark Farben (angepasst für Fyne)
|
|
||||||
var (
|
var (
|
||||||
// Basisfarben
|
|
||||||
OneDarkBackground = color.NRGBA{R: 40, G: 44, B: 52, A: 0xff} // #282c34 (helleres Grau)
|
OneDarkBackground = color.NRGBA{R: 40, G: 44, B: 52, A: 0xff} // #282c34 (helleres Grau)
|
||||||
OneDarkCardBackground = color.NRGBA{R: 30, G: 32, B: 40, A: 0xff} // #1e2028 (dunkler für Cards)
|
OneDarkCardBackground = color.NRGBA{R: 30, G: 32, B: 40, A: 0xff} // #1e2028 (dunkler für Cards)
|
||||||
OneDarkText = color.NRGBA{R: 171, G: 178, B: 191, A: 0xff} // #abb2bf (Standard-Text)
|
OneDarkText = color.NRGBA{R: 171, G: 178, B: 191, A: 0xff} // #abb2bf (Standard-Text)
|
||||||
OneDarkSubtleText = color.NRGBA{R: 110, G: 115, B: 141, A: 0xff} // #6e738d (deaktiviert, Placeholder)
|
OneDarkSubtleText = color.NRGBA{R: 110, G: 115, B: 141, A: 0xff} // #6e738d (deaktiviert, Placeholder)
|
||||||
|
|
||||||
// Akzentfarben (One Dark)
|
|
||||||
OneDarkGreen = color.NRGBA{R: 152, G: 195, B: 121, A: 0xff} // #98c379
|
OneDarkGreen = color.NRGBA{R: 152, G: 195, B: 121, A: 0xff} // #98c379
|
||||||
OneDarkRed = color.NRGBA{R: 224, G: 108, B: 117, A: 0xff} // #e06c75
|
OneDarkRed = color.NRGBA{R: 224, G: 108, B: 117, A: 0xff} // #e06c75
|
||||||
OneDarkYellow = color.NRGBA{R: 229, G: 192, B: 123, A: 0xff} // #e5c07b
|
OneDarkYellow = color.NRGBA{R: 229, G: 192, B: 123, A: 0xff} // #e5c07b
|
||||||
)
|
)
|
||||||
|
|
||||||
// KettlebellThemeOneDark ist das angepasste Theme
|
|
||||||
type KettlebellThemeOneDark struct{}
|
type KettlebellThemeOneDark struct{}
|
||||||
|
|
||||||
func (t *KettlebellThemeOneDark) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
|
func (t *KettlebellThemeOneDark) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
|
||||||
|
|
@ -133,8 +50,6 @@ func (t *KettlebellThemeOneDark) Color(name fyne.ThemeColorName, variant fyne.Th
|
||||||
return OneDarkCardBackground
|
return OneDarkCardBackground
|
||||||
case theme.ColorNameFocus:
|
case theme.ColorNameFocus:
|
||||||
return OneDarkYellow
|
return OneDarkYellow
|
||||||
// case theme.ColorNameCardBackground:
|
|
||||||
// return OneDarkCardBackground
|
|
||||||
default:
|
default:
|
||||||
return theme.DefaultTheme().Color(name, variant)
|
return theme.DefaultTheme().Color(name, variant)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue