summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Jørgensen <julian@jtle.dk>2024-05-24 21:30:23 +0200
committerJulian Jørgensen <julian@jtle.dk>2024-05-24 21:30:23 +0200
commit2f83424ce8a0653e6e158318df1511daecf1a42d (patch)
treeb29c1b5c8df940873521d013697209195956151e
parent19f8454a680c5231df68fee36ed9758587df316c (diff)
Time format rework
-rw-r--r--db.go43
-rw-r--r--model.go59
-rw-r--r--page.go14
-rw-r--r--templates/parts/entry.html20
-rw-r--r--templates/parts/entryRows.html18
-rw-r--r--tidsreg.go62
6 files changed, 137 insertions, 79 deletions
diff --git a/db.go b/db.go
index ccee504..76cb4e7 100644
--- a/db.go
+++ b/db.go
@@ -6,17 +6,17 @@ import (
)
type Database interface {
- GetTracking() (*Task, error)
- GetTasks() ([]*Task, error)
- QueryTask(id *int) (*Task, error)
- StartNewEntry(now time.Time, task *Task) (*Task, error)
+ GetTracking() (*Entry, error)
+ GetTasks() ([]*Entry, error)
+ QueryTask(id *int) (*Entry, error)
+ StartNewEntry(now time.Time, task *Entry) (*Entry, error)
StopEntry(now time.Time) error
- SaveEntry(task *Task) (*Task, error)
+ SaveEntry(task *Entry) (*Entry, error)
}
type InMemDb struct {
- tasks []*Task
- current *Task
+ tasks []*Entry
+ current *Entry
}
var (
@@ -26,17 +26,17 @@ var (
func NewInMemDb() Database {
return &InMemDb {
- tasks: []*Task{},
+ tasks: []*Entry{},
current: nil,
}
}
-func (db *InMemDb) GetTracking() (*Task, error) {
+func (db *InMemDb) GetTracking() (*Entry, error) {
return db.current, nil
}
-func (db *InMemDb) GetTasks() ([]*Task, error) {
- res := make([]*Task, 0, len(db.tasks))
+func (db *InMemDb) GetTasks() ([]*Entry, error) {
+ res := make([]*Entry, 0, len(db.tasks))
for _, task := range db.tasks {
res = append(res, task)
}
@@ -44,18 +44,15 @@ func (db *InMemDb) GetTasks() ([]*Task, error) {
return res, nil
}
-func (db *InMemDb) QueryTask(id *int) (*Task, error) {
- for _, task := range db.tasks {
- if id != nil && task.Id == *id {
- return task, nil
- }
+func (db *InMemDb) QueryTask(id *int) (*Entry, error) {
+ if id != nil && *id >= 0 || *id < len(db.tasks) {
+ return db.tasks[*id], nil
}
-
return nil, ErrNotFound
}
-func (db *InMemDb) StartNewEntry(now time.Time, task *Task) (*Task, error) {
- newTask := &Task {
+func (db *InMemDb) StartNewEntry(now time.Time, task *Entry) (*Entry, error) {
+ newTask := &Entry {
Id: len(db.tasks),
From: task.From,
To: nil,
@@ -64,7 +61,8 @@ func (db *InMemDb) StartNewEntry(now time.Time, task *Task) (*Task, error) {
}
if newTask.From == nil {
- newTask.From = &now
+ from := TimeFromStd(now)
+ newTask.From = &from
}
db.tasks = append(db.tasks, newTask)
@@ -75,7 +73,8 @@ func (db *InMemDb) StartNewEntry(now time.Time, task *Task) (*Task, error) {
func (db *InMemDb) StopEntry(now time.Time) error {
if db.current != nil {
- db.current.To = &now
+ to := TimeFromStd(now)
+ db.current.To = &to
db.current = nil
} else {
return ErrNotRunning
@@ -83,7 +82,7 @@ func (db *InMemDb) StopEntry(now time.Time) error {
return nil
}
-func (db *InMemDb) SaveEntry(task *Task) (*Task, error) {
+func (db *InMemDb) SaveEntry(task *Entry) (*Entry, error) {
if task.Id < 0 || task.Id >= len(db.tasks) {
copyTask := *task
copyTask.Id = len(db.tasks)
diff --git a/model.go b/model.go
index 0f52ef9..b6d5c79 100644
--- a/model.go
+++ b/model.go
@@ -1,11 +1,62 @@
package main
-import "time"
+import (
+ "errors"
+ "regexp"
+ "time"
+)
-type Task struct {
+type Entry struct {
Id int
- From *time.Time
- To *time.Time
+ Date time.Time
+ From *Time
+ To *Time
Tag *string
Comment string
}
+
+type Date string
+
+type Time string
+
+var timeMatch = regexp.MustCompile("\\d\\d:\\d\\d")
+var dateMatch = regexp.MustCompile("\\d\\d\\d\\d-\\d\\d-\\d\\d")
+
+var (
+ ErrInvalidTime = errors.New("Invalid time string")
+ ErrInvalidDate = errors.New("Invalid date string")
+)
+
+func TimeFromStd(time time.Time) Time {
+ return Time(time.Format("15:04"))
+}
+
+func TimeFromString(str string) (*Time, error) {
+ if str == "" {
+ return nil, nil
+ }
+
+ matched := timeMatch.MatchString(str)
+ if !matched {
+ return nil, ErrInvalidTime
+ }
+ time := Time(str)
+ return &time, nil
+}
+
+func DateFromStd(time time.Time) Date {
+ return Date(time.Format("2006-01-02"))
+}
+
+func DateFromString(str string) (*Date, error) {
+ if str == "" {
+ return nil, nil
+ }
+
+ matched := dateMatch.MatchString(str)
+ if !matched {
+ return nil, ErrInvalidTime
+ }
+ date := Date(str)
+ return &date, nil
+}
diff --git a/page.go b/page.go
index 384ca57..c3ab160 100644
--- a/page.go
+++ b/page.go
@@ -5,14 +5,14 @@ type Service struct {
}
type EntryPage struct {
- Task *Task
+ Entry *Entry
Detached bool
- Tracking *Task
+ Tracking *Entry
}
type RootPage struct {
Entry *EntryPage
- Tasks []*Task
+ Entries []*Entry
}
func NewService(db Database) *Service {
@@ -21,20 +21,20 @@ func NewService(db Database) *Service {
}
}
-func (srv *Service) GetEntryPage(detached *Task) (*EntryPage, error) {
+func (srv *Service) GetEntryPage(detached *Entry) (*EntryPage, error) {
tracking, err := srv.Db.GetTracking()
if err != nil {
return nil, err
}
page := &EntryPage {
- Task: tracking,
+ Entry: tracking,
Detached: false,
Tracking: tracking,
}
if detached != nil {
- page.Task = detached
+ page.Entry = detached
page.Detached = true
}
@@ -54,6 +54,6 @@ func (srv *Service) GetRootPage() (*RootPage, error) {
return &RootPage {
Entry: entry,
- Tasks: tasks,
+ Entries: tasks,
}, nil
}
diff --git a/templates/parts/entry.html b/templates/parts/entry.html
index f99fb90..9f05ffa 100644
--- a/templates/parts/entry.html
+++ b/templates/parts/entry.html
@@ -1,18 +1,18 @@
-<form id="entry-bar" autocomplete="off" class="status-{{if .Detached}}detached{{else}}{{if .Task}}started{{else}}stopped{{end}}{{end}}">
- <input style="display: none;" value="{{ if .Task }}{{ .Task.Id }}{{end}}" type="text" name="id" />
+<form id="entry-bar" autocomplete="off" class="status-{{if .Detached}}detached{{else}}{{if .Entry}}started{{else}}stopped{{end}}{{end}}">
+ <input style="display: none;" value="{{ if .Entry }}{{ .Entry.Id }}{{end}}" type="text" name="id" />
{{ if .Detached }}
- <span>{{ if gt .Task.Id -1 }}Redigerer opgave {{ .Task.Id }}{{else}}Redigerer ny opgave{{end}}{{ if .Tracking }}, med opgave i baggrunden!{{else}}.{{end}}</span><br>
+ <span>{{ if gt .Entry.Id -1 }}Redigerer opgave {{ .Entry.Id }}{{else}}Redigerer ny opgave{{end}}{{ if .Tracking }}, med opgave i baggrunden!{{else}}.{{end}}</span><br>
{{end}}
<div class="flex just-start">
<div class="entry-box">
<b>Interval</b>
<div>
<label for="fromTime">Fra: </label>
- <input name="from" id="fromTime" type="time" class="form-control" {{if .Task}}value="{{formatTime .Task.From}}" required{{end}} aria-label="Time start">
+ <input name="from" id="fromTime" type="time" class="form-control" {{if .Entry}}value="{{.Entry.From}}" required{{end}} aria-label="Time start">
</div>
<div>
<label for="toTime">Til: </label>
- <input name="to" id="toTime" type="time" class="form-control" {{if not .Detached}}disabled{{end}} aria-label="Time stop">
+ <input name="to" id="toTime" type="time" class="form-control" {{if .Entry}}value="{{if .Entry.To}}{{.Entry.To}}{{end}}" required{{end}} {{if not .Detached}}disabled{{end}} aria-label="Time stop">
</div>
</div>
<div class="entry-box">
@@ -30,16 +30,16 @@
</div>
<div class="entry-box">
<b>Kommentar</b><br>
- <textarea name="comment">{{if .Task}}{{.Task.Comment}}{{end}}</textarea>
+ <textarea name="comment">{{if .Entry}}{{.Entry.Comment}}{{end}}</textarea>
</div>
<div class="entry-box">
<b>Status</b><br>
- {{ if not .Detached}}<i>{{ if .Task }}I gang{{ else }}Stoppet{{ end }}</i><br>{{end}}
- {{ if .Task }}<span>1:34 timer</span>{{ end }}
+ {{ if not .Detached}}<i>{{ if .Entry }}I gang{{ else }}Stoppet{{ end }}</i><br>{{end}}
+ {{ if .Entry }}<span>1:34 timer</span>{{ end }}
</div>
</div>
- {{ if .Task }}
+ {{ if .Entry }}
<button
hx-put="/save{{if .Detached}}?detached=true{{end}}"
hx-trigger="click"
@@ -56,7 +56,7 @@
hx-swap="outerHTML"
>Tilbage</button>
{{ else }}
- {{ if .Task }}
+ {{ if .Entry }}
<button
hx-post="/stop"
hx-trigger="click"
diff --git a/templates/parts/entryRows.html b/templates/parts/entryRows.html
index 1b364e3..fd14608 100644
--- a/templates/parts/entryRows.html
+++ b/templates/parts/entryRows.html
@@ -1,12 +1,14 @@
-<tbody hx-trigger="changedTasks from:body" hx-get="/entryRows">
- {{ range $task := .Tasks }}
+<tbody hx-swap="outerHTML" hx-trigger="changedEntries from:body" hx-get="/entryRows">
+ {{ range $entry := .Entries }}
<tr>
- <td>{{ $task.Id }}</td>
- <td><input type="time" disabled value="{{formatTime $task.From }}" /></td>
- <td><input type="time" disabled value="{{formatTime $task.To }}" /></td>
- <td>{{ if $task.Tag}}{{ $task.Tag }}{{end}}</td>
- <td>{{ $task.Comment }}</td>
- <td></td>
+ <td>{{ $entry.Id }}</td>
+ <td><input type="time" disabled value="{{$entry.From }}" /></td>
+ <td><input type="time" disabled value="{{if $entry.To}}{{$entry.To }}{{end}}" /></td>
+ <td>{{ if $entry.Tag}}{{ $entry.Tag }}{{end}}</td>
+ <td>{{ $entry.Comment }}</td>
+ <td>
+ {{ if $entry.To }}<button hx-trigger="click" hx-swap="outerHTML" hx-target="#entry-bar" hx-get="/edit?id={{$entry.Id}}">e</button>{{end}}
+ </td>
</tr>
{{ end }}
</tbody>
diff --git a/tidsreg.go b/tidsreg.go
index 410a5b5..28dea95 100644
--- a/tidsreg.go
+++ b/tidsreg.go
@@ -16,32 +16,17 @@ type Server struct {
srv *Service
}
-func funcMapFormatTime(t *time.Time) string {
- if t == nil {
- return ""
- }
- return t.Format("15:04")
-}
-
-func parseStringTime(str string) (*time.Time, error) {
- if str == "" {
- return nil, nil
- }
- t, err := time.Parse("15:04", str)
- return &t, err
-}
-
-func parseTaskFromForm(r *http.Request) (*Task, error) {
+func parseTaskFromForm(r *http.Request) (*Entry, error) {
err := r.ParseForm()
if err != nil {
return nil, err
}
- from, err := parseStringTime(r.PostFormValue("from"))
+ from, err := TimeFromString(r.PostFormValue("from"))
if err != nil {
return nil, err
}
- to, err := parseStringTime(r.PostFormValue("to"))
+ to, err := TimeFromString(r.PostFormValue("to"))
if err != nil {
return nil, err
}
@@ -55,7 +40,7 @@ func parseTaskFromForm(r *http.Request) (*Task, error) {
}
}
- task := &Task {
+ task := &Entry {
Id: int(id),
From: from,
To: to,
@@ -65,10 +50,7 @@ func parseTaskFromForm(r *http.Request) (*Task, error) {
}
func (s *Server) renderTemplate(w http.ResponseWriter, name string, page interface{}) {
- tmpl := template.New("").Funcs(template.FuncMap {
- "formatTime": funcMapFormatTime,
- })
- tmpl, err := tmpl.ParseFiles("templates/index.html", "templates/parts/entry.html", "templates/parts/entryRows.html")
+ tmpl, err := template.ParseFiles("templates/index.html", "templates/parts/entry.html", "templates/parts/entryRows.html")
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
@@ -123,7 +105,7 @@ func (s *Server) postStart(w http.ResponseWriter, r *http.Request) {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
- w.Header().Add("HX-Trigger", "changedTasks")
+ w.Header().Add("HX-Trigger", "changedEntries")
s.renderTemplate(w, "entry.html", page)
}
@@ -146,7 +128,7 @@ func (s *Server) postStop(w http.ResponseWriter, r *http.Request) {
return
}
- w.Header().Add("HX-Trigger", "changedTasks")
+ w.Header().Add("HX-Trigger", "changedEntries")
s.getTracking(w, r)
}
@@ -174,7 +156,7 @@ func (s *Server) putSave(w http.ResponseWriter, r *http.Request) {
return
}
- var detached *Task = nil
+ var detached *Entry = nil
if r.URL.Query().Get("detached") == "true" {
detached = task
}
@@ -185,13 +167,13 @@ func (s *Server) putSave(w http.ResponseWriter, r *http.Request) {
return
}
- w.Header().Add("HX-Trigger", "changedTasks")
+ w.Header().Add("HX-Trigger", "changedEntries")
s.renderTemplate(w, "entry.html", page)
}
func (s *Server) getNew(w http.ResponseWriter, _ *http.Request) {
- task := &Task {
+ task := &Entry {
Id: -1,
From: nil,
To: nil,
@@ -208,6 +190,29 @@ func (s *Server) getNew(w http.ResponseWriter, _ *http.Request) {
s.renderTemplate(w, "entry.html", page)
}
+func (s *Server) getEdit(w http.ResponseWriter, r *http.Request) {
+ idLarge, err := strconv.ParseInt(r.URL.Query().Get("id"), 10, 32)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ id := int(idLarge)
+
+ task, err := s.srv.Db.QueryTask(&id)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ page, err := s.srv.GetEntryPage(task)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ s.renderTemplate(w, "entry.html", page)
+}
+
func (s *Server) getEntries(w http.ResponseWriter, _ *http.Request) {
page, err := s.srv.GetRootPage()
if err != nil {
@@ -233,6 +238,7 @@ func main() {
r.HandleFunc("/stop", s.postStop).Methods("POST")
r.HandleFunc("/save", s.putSave).Methods("PUT")
r.HandleFunc("/entryRows", s.getEntries).Methods("GET")
+ r.HandleFunc("/edit", s.getEdit).Methods("GET")
r.HandleFunc("/", s.rootHandle)
http.Handle("/", r)