summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2024-05-26 22:20:40 +0200
committerJulian T <julian@jtle.dk>2024-05-29 22:25:47 +0200
commitafb0a04fe600575b00d551da7dce6a08679ef7b8 (patch)
tree8f90720887cf49045d65a9d17dad984dae06275c
parent9b55b16f3b906958171b4f45002b58d5f77b9705 (diff)
Working task selectionHEADmain
-rw-r--r--db.go4
-rw-r--r--model.go13
-rw-r--r--page.go61
-rw-r--r--templates/index.html13
-rw-r--r--templates/parts/entry.html37
-rw-r--r--templates/parts/entryRows.html2
-rw-r--r--templates/parts/taskEntry.html8
-rw-r--r--templates/parts/taskRows.html17
-rw-r--r--tidsreg.go160
9 files changed, 258 insertions, 57 deletions
diff --git a/db.go b/db.go
index 363bc93..b37e103 100644
--- a/db.go
+++ b/db.go
@@ -70,7 +70,7 @@ func (db *InMemDb) StartNewEntry(now time.Time, task *Entry) (*Entry, error) {
From: task.From,
To: nil,
Date: DateFromStd(now),
- Tag: task.Tag,
+ Tasks: task.Tasks,
Comment: task.Comment,
}
@@ -108,8 +108,8 @@ func (db *InMemDb) SaveEntry(entry *Entry) (*Entry, error) {
existent := db.entries[entry.Id]
existent.Comment = entry.Comment
- existent.Tag = entry.Tag
existent.From = entry.From
+ existent.Tasks = entry.Tasks
existent.To = entry.To
existent.Date = entry.Date
return existent, nil
diff --git a/model.go b/model.go
index 93a988c..6eebd60 100644
--- a/model.go
+++ b/model.go
@@ -12,7 +12,7 @@ type Entry struct {
Date Date
From *Time
To *Time
- Tag *string
+ Tasks map[int]*time.Duration
Comment string
}
@@ -27,8 +27,13 @@ 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")
+type EntrySubDuration string
+
+var (
+ timeMatch = regexp.MustCompile("[0-9]{2}:[0-9]{2}")
+ dateMatch = regexp.MustCompile("[0-9]{4}-[0-9]{2}-[0-9]{2}")
+ subDurationMatch = regexp.MustCompile("[0-9]")
+)
var (
ErrInvalidTime = errors.New("Invalid time string")
@@ -41,7 +46,7 @@ func NewEntry(date Date) *Entry {
From: nil,
Date: date,
To: nil,
- Tag: nil,
+ Tasks: map[int]*time.Duration{},
Comment: "",
}
diff --git a/page.go b/page.go
index 8e2074e..cae7ecb 100644
--- a/page.go
+++ b/page.go
@@ -1,6 +1,8 @@
package main
-import "time"
+import (
+ "time"
+)
type Service struct {
Db Database
@@ -14,10 +16,12 @@ type DateInfo struct {
}
type EntryPage struct {
- Entry *Entry
- Detached bool
- Tracking *Entry
- DateInfo DateInfo
+ Entry *Entry
+ Detached bool
+ Tracking *Entry
+ DateInfo DateInfo
+ GroupToTasks map[string]([]*Task)
+ TaskCount int
}
type RootPage struct {
@@ -25,11 +29,12 @@ type RootPage struct {
Entry *EntryPage
Entries []*Entry
Task *TaskPage
- Tasks []*Task
+ Tasks *TasksPage
}
type TasksPage struct {
- Tasks []*Task
+ Tasks []*Task
+ GroupToTasks map[string]([]*Task)
}
type TaskPage struct {
@@ -73,8 +78,26 @@ func (srv *Service) GetTasksPage() (*TasksPage, error) {
if err != nil {
return nil, err
}
+
+ mp := make(map[string]([]*Task))
+
+ for _, task := range tasks {
+ key := task.Group
+ if key == nil {
+ empty := ""
+ key = &empty
+ }
+ mparr, ok := mp[*key]
+ if !ok {
+ mparr = make([]*Task, 0)
+ }
+ mparr = append(mparr, task)
+ mp[*key] = mparr
+ }
+
return &TasksPage{
- Tasks: tasks,
+ Tasks: tasks,
+ GroupToTasks: mp,
}, nil
}
@@ -83,11 +106,19 @@ func (srv *Service) GetEntryPage(detached *Entry, dt DateInfo) (*EntryPage, erro
if err != nil {
return nil, err
}
+
+ tasksPage, err := srv.GetTasksPage()
+ if err != nil {
+ return nil, err
+ }
+
page := &EntryPage{
- Entry: tracking,
- Detached: false,
- Tracking: tracking,
- DateInfo: dt,
+ Entry: tracking,
+ Detached: false,
+ Tracking: tracking,
+ DateInfo: dt,
+ GroupToTasks: tasksPage.GroupToTasks,
+ TaskCount: 0,
}
if detached != nil {
@@ -95,6 +126,10 @@ func (srv *Service) GetEntryPage(detached *Entry, dt DateInfo) (*EntryPage, erro
page.Detached = true
}
+ if page.Entry != nil {
+ page.TaskCount = len(page.Entry.Tasks)
+ }
+
return page, nil
}
@@ -115,7 +150,7 @@ func (srv *Service) GetRootPage(date Date) (*RootPage, error) {
return nil, err
}
- tasks, err := srv.Db.GetTasks()
+ tasks, err := srv.GetTasksPage()
if err != nil {
return nil, err
}
diff --git a/templates/index.html b/templates/index.html
index 18ca5f0..99bfc31 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -8,6 +8,7 @@
<!-- TODO REMOVE -->
<script src="https://unpkg.com/htmx.org@1.9.12/dist/htmx.js" integrity="sha384-qbtR4rS9RrUMECUWDWM2+YGgN3U4V4ZncZ0BvUcg9FGct0jqXz3PUdVpU1p0yrXS" crossorigin="anonymous"></script>
+ <script src="https://unpkg.com/htmx.org@1.9.12/dist/ext/response-targets.js"></script>
<style>
.flex {
@@ -73,16 +74,22 @@
padding: 10px;
max-width: 500px;
}
+
+#error-box {
+ color: red;
+ font-weight: bold;
+}
</style>
</head>
- <body>
+ <body hx-ext="response-targets">
<div id="controls-bar">
<button hx-get="/" hx-replace-url="true" hx-params="date" hx-vals='{"date": "{{.DateInfo.Yesterday}}"}' hx-target="body">I går</button>
<input type="date" hx-get="/" hx-replace-url="true" hx-params="date" hx-vals='js:{date: event.target.value}' autocomplete="off" hx-target="body" hx-trigger="change" value="{{.DateInfo.Date}}" />
<button hx-get="/" hx-replace-url="true" hx-params="date" hx-vals='{"date": "{{.DateInfo.Tomorrow}}"}' hx-target="body">I morgen</button>
+ <div id="error-box"></div>
</div>
- <div class="flex">
+ <div class="flex" hx-target-*="#error-box">
<div id="entry-bar">
{{template "entry.html" .Entry}}
<div>
@@ -120,7 +127,7 @@
</select>
</form>
{{ template "task.html" .Task }}
- {{ template "taskRows.html" . }}
+ {{ template "taskRows.html" .Tasks }}
</div>
</div>
</body>
diff --git a/templates/parts/entry.html b/templates/parts/entry.html
index 52f2eb3..4be2073 100644
--- a/templates/parts/entry.html
+++ b/templates/parts/entry.html
@@ -16,17 +16,32 @@
</div>
</div>
<div class="entry-box">
- <b>Mærker</b><br>
- <select>
- <option value="-">-</option>
- <option value="SVT-232">SVT-232</option>
- <option value="Ferie">Ferie</option>
- </select><br>
- <select>
- <option value="-">-</option>
- <option value="SVT-232">SVT-232</option>
- <option value="Ferie">Ferie</option>
- </select>
+ <b>Opgaver</b><br>
+ <input readonly style="display: none;" name="task-count" value="{{ .TaskCount }}" />
+ <table>
+ <tbody>
+ {{ if .Entry }}
+ {{ range $x := taskMapToArr .Entry.Tasks }}
+ <tr>
+ <td>
+ <select name="task-{{$x.Key}}-id" hx-trigger="changedTasks from:body" hx-get="/task/select?selected={{$x.Id}}">
+ {{ template "taskEntry.html" arr $.GroupToTasks $x.Id }}
+ </select>
+ </td>
+ <td><input name="task-{{$x.Key}}-part" type="text" class="short" {{if $x.Sel}}value="{{$x.Sel}}"{{end}}/></td>
+ </tr>
+ {{ end }}
+ {{ end }}
+ <tr>
+ <td>
+ <select name="task-new-id" hx-trigger="changedTasks from:body" hx-get="/task/select">
+ {{ template "taskEntry.html" (arr .GroupToTasks -1) }}
+ </select>
+ </td>
+ <td><input disabled name="task-new-part" type="text" class="short" /></td>
+ </tr>
+ </tbody>
+ </table>
</div>
<div class="entry-box">
<b>Kommentar</b><br>
diff --git a/templates/parts/entryRows.html b/templates/parts/entryRows.html
index 8f10ee3..3749ddb 100644
--- a/templates/parts/entryRows.html
+++ b/templates/parts/entryRows.html
@@ -4,7 +4,7 @@
<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></td>
<td>{{ $entry.Comment }}</td>
<td>
{{ if $entry.To }}<button hx-trigger="click" hx-swap="outerHTML" hx-target="#entry-form" hx-get="/edit?id={{$entry.Id}}">e</button>{{end}}
diff --git a/templates/parts/taskEntry.html b/templates/parts/taskEntry.html
new file mode 100644
index 0000000..3c3be3f
--- /dev/null
+++ b/templates/parts/taskEntry.html
@@ -0,0 +1,8 @@
+<option>-</option>
+{{ range $group, $tasks := index . 0}}
+<optgroup label="{{$group}}">
+ {{ range $task := $tasks }}
+ <option {{if eq $task.Id (index $ 1)}}selected{{end}} value="{{$task.Id}}">{{$task.Text}}</option>
+ {{ end }}
+</optgroup>
+{{ end }}
diff --git a/templates/parts/taskRows.html b/templates/parts/taskRows.html
index a2a6020..bef2366 100644
--- a/templates/parts/taskRows.html
+++ b/templates/parts/taskRows.html
@@ -1,11 +1,20 @@
<table>
<tbody hx-swap="outerHTML" hx-trigger="changedTasks from:body" hx-get="/task/list">
- {{ range $task := .Tasks }}
+ {{ range $group, $tasks := .GroupToTasks }}
<tr>
- <td>Trifork</td>
- <td />
- <td />
+ <td>{{ $group }}</td>
+ <td></td>
+ <td></td>
+ <td></td>
</tr>
+ {{ range $task := $tasks }}
+ <tr>
+ <td></td>
+ <td>{{ $task.Text }}</td>
+ <td>{{ if $task.Ident }}{{ $task.Ident }}{{end}}</td>
+ <td><button hx-trigger="click" hx-swap="outerHTML" hx-target="#taskForm" hx-get="/task/edit?id={{$task.Id}}" >e</button></td>
+ </tr>
+ {{ end }}
{{ end }}
</tbody>
</table>
diff --git a/tidsreg.go b/tidsreg.go
index e327f74..b988ad7 100644
--- a/tidsreg.go
+++ b/tidsreg.go
@@ -18,20 +18,27 @@ type Server struct {
srv *Service
}
+func parseIdInt(str string) (int, error) {
+ var id int = -1
+ if str != "" {
+ idLong, err := strconv.ParseInt(str, 10, 32)
+ if err != nil {
+ return -1, err
+ }
+ id = int(idLong)
+ }
+ return id, nil
+}
+
func parseTaskFromForm(r *http.Request) (*Task, error) {
err := r.ParseForm()
if err != nil {
return nil, err
}
- var id int64
- if r.PostFormValue("id") == "" {
- id = -1
- } else {
- id, err = strconv.ParseInt(r.PostFormValue("id"), 10, 32)
- if err != nil {
- return nil, err
- }
+ id, err := parseIdInt(r.PostFormValue("id"))
+ if err != nil {
+ return nil, err
}
var group *string
@@ -57,12 +64,59 @@ func parseTaskFromForm(r *http.Request) (*Task, error) {
}, nil
}
+func readTaskSelectFromPost(r *http.Request, index string) (int, *time.Duration, bool, error) {
+ idS := r.PostFormValue(fmt.Sprintf("task-%s-id", index))
+ if idS == "" || idS == "-" {
+ return -1, nil, false, nil
+ }
+ id, err := parseIdInt(idS)
+ if err != nil {
+ return -1, nil, false, err
+ }
+
+ durS := r.PostFormValue(fmt.Sprintf("task-%s-part", index))
+ if durS == "" {
+ return id, nil, true, nil
+ }
+ dur, err := time.ParseDuration(durS)
+ if err != nil {
+ return -1, nil, false, err
+ }
+
+ return id, &dur, true, nil
+}
+
func parseEntryFromForm(r *http.Request, useToday bool) (*Entry, error) {
err := r.ParseForm()
if err != nil {
return nil, err
}
+ tasks := map[int]*time.Duration{}
+ taskCount, err := strconv.ParseInt(r.PostFormValue("task-count"), 10, 32)
+ if err != nil {
+ return nil, fmt.Errorf("Error reading task-count: %w", err)
+ }
+ for i := 0; i < int(taskCount); i++ {
+ id, dur, ok, err := readTaskSelectFromPost(r, fmt.Sprintf("%d", i))
+ if err != nil {
+ return nil, err
+ }
+ if !ok {
+ continue
+ }
+ tasks[id] = dur
+ }
+ {
+ id, dur, ok, err := readTaskSelectFromPost(r, "new")
+ if err != nil {
+ return nil, fmt.Errorf("Error reading new task select: %w", err)
+ }
+ if ok {
+ tasks[id] = dur
+ }
+ }
+
var date Date
dateP, err := DateFromString(r.PostFormValue("date"))
if err != nil {
@@ -85,28 +139,51 @@ func parseEntryFromForm(r *http.Request, useToday bool) (*Entry, error) {
if err != nil {
return nil, err
}
- var id int64
- if r.PostFormValue("id") == "" {
- id = -1
- } else {
- id, err = strconv.ParseInt(r.PostFormValue("id"), 10, 32)
- if err != nil {
- return nil, err
- }
+ id, err := parseIdInt(r.PostFormValue("id"))
+ if err != nil {
+ return nil, err
}
+ log.Printf("hej hej %+v\n", tasks)
task := &Entry{
Id: int(id),
From: from,
Date: date,
To: to,
+ Tasks: tasks,
Comment: r.PostFormValue("comment"),
}
return task, nil
}
func (s *Server) renderTemplate(w http.ResponseWriter, name string, page interface{}) {
- tmpl, err := template.ParseFiles("templates/index.html", "templates/parts/entry.html", "templates/parts/entryRows.html", "templates/parts/task.html", "templates/parts/taskRows.html")
+ tmpl := template.New("").Funcs(template.FuncMap {
+ "arr": (func (els ...interface{}) []interface{} {
+ return els
+ }),
+ "taskMapToArr": (func (taskMap map[int]*time.Duration) [](struct {Key int; Id int; Sel *string}) {
+ res := make([](struct {Key int; Id int; Sel *string}), 0, len(taskMap))
+ count := 0
+ for taskId, sel := range taskMap {
+ thing := struct {Key int; Id int; Sel *string}{
+ Key: count,
+ Id: taskId,
+ Sel: nil,
+ }
+ if sel != nil {
+ formatted := sel.String()
+ thing.Sel = &formatted
+ }
+ res = append(res, thing)
+
+ count += 1
+ }
+
+ fmt.Printf("Hej, %+v, lul: %+v\n", res, taskMap)
+ return res
+ }),
+ })
+ tmpl, err := tmpl.ParseFiles("templates/index.html", "templates/parts/entry.html", "templates/parts/entryRows.html", "templates/parts/task.html", "templates/parts/taskRows.html", "templates/parts/taskEntry.html")
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
@@ -269,12 +346,11 @@ func (s *Server) getNew(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) getEdit(w http.ResponseWriter, r *http.Request) {
- idLarge, err := strconv.ParseInt(r.URL.Query().Get("id"), 10, 32)
+ id, err := parseIdInt(r.URL.Query().Get("id"))
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
- id := int(idLarge)
entries, err := s.srv.Db.QueryEntry(&id, nil)
if err != nil {
@@ -359,6 +435,50 @@ func (s *Server) getTaskEmpty(w http.ResponseWriter, _ *http.Request) {
s.renderTemplate(w, "task.html", page)
}
+func (s *Server) getTaskEdit(w http.ResponseWriter, r *http.Request) {
+ id, err := parseIdInt(r.URL.Query().Get("id"))
+ if err != nil {
+ writeError(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ page, err := s.srv.GetTaskPage(&id, nil)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ s.renderTemplate(w, "task.html", page)
+}
+
+func (s *Server) getTaskSelect(w http.ResponseWriter, r *http.Request) {
+ page, err := s.srv.GetTasksPage()
+ if err != nil {
+ writeError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ var selected int = -1
+ {
+ selectedStr := r.URL.Query().Get("selected")
+ if selectedStr != "" {
+ selectedLong, err := strconv.ParseInt(selectedStr, 10, 32)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ selected = int(selectedLong)
+ }
+ }
+
+ args := []interface{}{
+ &page.GroupToTasks,
+ selected,
+ }
+
+ s.renderTemplate(w, "taskEntry.html", args)
+}
+
func main() {
fmt.Println("Hello world!")
@@ -378,6 +498,8 @@ func main() {
r.HandleFunc("/task/list", s.getTasksList).Methods("GET")
r.HandleFunc("/task/save", s.postTaskSave).Methods("POST")
r.HandleFunc("/task/empty", s.getTaskEmpty).Methods("GET")
+ r.HandleFunc("/task/edit", s.getTaskEdit).Methods("GET")
+ r.HandleFunc("/task/select", s.getTaskSelect).Methods("GET")
r.HandleFunc("/", s.rootHandle)
http.Handle("/", r)