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
|
||||
}
|
||||
|
||||
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 (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
|
|
@ -17,33 +18,137 @@ func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.Canvas
|
|||
var history []data.TrainingSession
|
||||
|
||||
placeholder := widget.NewLabel("Noch keine Trainingsdaten vorhanden.")
|
||||
|
||||
var showDetailDialog func(session data.TrainingSession, id int)
|
||||
var refreshData func()
|
||||
|
||||
list := widget.NewList(
|
||||
func() int { return len(history) },
|
||||
func() fyne.CanvasObject {
|
||||
stats := container.NewGridWithColumns(3,
|
||||
container.NewVBox(widget.NewLabel("Sätze"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
||||
container.NewVBox(widget.NewLabel("Dauer"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
||||
container.NewVBox(widget.NewLabel("Reps/Satz"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
||||
return container.NewGridWithColumns(4,
|
||||
widget.NewLabel(""),
|
||||
widget.NewLabel(""),
|
||||
widget.NewLabel(""),
|
||||
widget.NewLabel(""),
|
||||
)
|
||||
return widget.NewCard("", "", container.NewVBox(widget.NewLabel(""), widget.NewSeparator(), stats))
|
||||
},
|
||||
func(i widget.ListItemID, o fyne.CanvasObject) {
|
||||
session := history[i]
|
||||
card := o.(*widget.Card)
|
||||
card.SetTitle(session.Date.Format("02.01.2006 15:04"))
|
||||
|
||||
content := card.Content.(*fyne.Container)
|
||||
programLabel := content.Objects[0].(*widget.Label)
|
||||
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))
|
||||
row := o.(*fyne.Container)
|
||||
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))
|
||||
row.Objects[2].(*widget.Label).SetText(utils.FormatDuration(session.Duration))
|
||||
row.Objects[3].(*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
|
||||
history, err = db.GetHistory()
|
||||
if err != nil {
|
||||
|
|
@ -61,8 +166,15 @@ func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.Canvas
|
|||
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))
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
|
|
@ -86,21 +7,17 @@ import (
|
|||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
// One Dark Farben (angepasst für Fyne)
|
||||
var (
|
||||
// Basisfarben
|
||||
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)
|
||||
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)
|
||||
|
||||
// Akzentfarben (One Dark)
|
||||
OneDarkGreen = color.NRGBA{R: 152, G: 195, B: 121, A: 0xff} // #98c379
|
||||
OneDarkRed = color.NRGBA{R: 224, G: 108, B: 117, A: 0xff} // #e06c75
|
||||
OneDarkYellow = color.NRGBA{R: 229, G: 192, B: 123, A: 0xff} // #e5c07b
|
||||
)
|
||||
|
||||
// KettlebellThemeOneDark ist das angepasste Theme
|
||||
type KettlebellThemeOneDark struct{}
|
||||
|
||||
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
|
||||
case theme.ColorNameFocus:
|
||||
return OneDarkYellow
|
||||
// case theme.ColorNameCardBackground:
|
||||
// return OneDarkCardBackground
|
||||
default:
|
||||
return theme.DefaultTheme().Color(name, variant)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue