summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Jørgensen <julian@jtle.dk>2024-05-25 11:53:43 +0200
committerJulian Jørgensen <julian@jtle.dk>2024-05-25 11:53:43 +0200
commit2610f9b67e4a915859c029da9b17e9d262b6564e (patch)
tree4504bf2d995703f583fff199b1ae7e54eef4e432
parent6cdd02af0684500b5376d4d4a08feb796fdaf889 (diff)
Add task ui and editingold
-rw-r--r--db.go42
-rw-r--r--model.go17
-rw-r--r--page.go38
-rw-r--r--templates/index.html62
-rw-r--r--templates/parts/entry.html12
-rw-r--r--templates/parts/entryRows.html2
-rw-r--r--templates/parts/task.html18
-rw-r--r--templates/parts/taskRows.html11
-rw-r--r--tidsreg.go92
9 files changed, 235 insertions, 59 deletions
diff --git a/db.go b/db.go
index 45296d6..363bc93 100644
--- a/db.go
+++ b/db.go
@@ -19,9 +19,9 @@ type Database interface {
}
type InMemDb struct {
- entries []*Entry
+ entries []*Entry
current *Entry
- tasks []*Task
+ tasks []*Task
}
var (
@@ -30,7 +30,7 @@ var (
)
func NewInMemDb() Database {
- return &InMemDb {
+ return &InMemDb{
entries: []*Entry{},
current: nil,
}
@@ -65,12 +65,12 @@ func (db *InMemDb) QueryEntry(id *int, date *Date) ([]*Entry, error) {
}
func (db *InMemDb) StartNewEntry(now time.Time, task *Entry) (*Entry, error) {
- newTask := &Entry {
- Id: len(db.entries),
- From: task.From,
- To: nil,
- Date: DateFromStd(now),
- Tag: task.Tag,
+ newTask := &Entry{
+ Id: len(db.entries),
+ From: task.From,
+ To: nil,
+ Date: DateFromStd(now),
+ Tag: task.Tag,
Comment: task.Comment,
}
@@ -139,8 +139,26 @@ func (db *InMemDb) QueryTask(id *int, group *string, ident *string) ([]*Task, er
res = append(res, task)
}
- return res, ErrNotFound
+ return res, nil
}
-func (db *InMemDb) SaveTask(task *Task) (*Task, error) {
- return nil, nil
+func (db *InMemDb) SaveTask(task *Task) (newTask *Task, err error) {
+ var tasks []*Task
+
+ tasks, err = db.QueryTask(&task.Id, nil, nil)
+ if err != nil {
+ return
+ }
+ if len(tasks) > 0 {
+ newTask = tasks[0]
+ newTask.Text = task.Text
+ newTask.Group = task.Group
+ newTask.Ident = task.Ident
+ } else {
+ taskCopy := *task
+ taskCopy.Id = len(db.tasks)
+ db.tasks = append(db.tasks, &taskCopy)
+ newTask = &taskCopy
+ }
+
+ return
}
diff --git a/model.go b/model.go
index b8d174c..93a988c 100644
--- a/model.go
+++ b/model.go
@@ -17,10 +17,10 @@ type Entry struct {
}
type Task struct {
- Id int
- Group *string
- Ident *string
- Text string
+ Id int
+ Group *string
+ Ident *string
+ Text string
}
type Date string
@@ -47,6 +47,15 @@ func NewEntry(date Date) *Entry {
}
+func NewTask() *Task {
+ return &Task{
+ Id: -1,
+ Group: nil,
+ Ident: nil,
+ Text: "",
+ }
+}
+
func TimeFromStd(time time.Time) Time {
return Time(time.Format("15:04"))
}
diff --git a/page.go b/page.go
index b58436d..8e2074e 100644
--- a/page.go
+++ b/page.go
@@ -24,12 +24,18 @@ type RootPage struct {
DateInfo DateInfo
Entry *EntryPage
Entries []*Entry
+ Task *TaskPage
+ Tasks []*Task
}
type TasksPage struct {
Tasks []*Task
}
+type TaskPage struct {
+ Task *Task
+}
+
func NewService(db Database) *Service {
return &Service{
Db: db,
@@ -45,6 +51,23 @@ func NewDateInfo(t time.Time) DateInfo {
}
}
+func (srv *Service) GetTaskPage(id *int, task *Task) (*TaskPage, error) {
+ if task == nil {
+ tasks, err := srv.Db.QueryTask(id, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ if len(tasks) < 1 {
+ return nil, ErrNotFound
+ }
+ task = tasks[0]
+ }
+
+ return &TaskPage{
+ Task: task,
+ }, nil
+}
+
func (srv *Service) GetTasksPage() (*TasksPage, error) {
tasks, err := srv.Db.GetTasks()
if err != nil {
@@ -54,6 +77,7 @@ func (srv *Service) GetTasksPage() (*TasksPage, error) {
Tasks: tasks,
}, nil
}
+
func (srv *Service) GetEntryPage(detached *Entry, dt DateInfo) (*EntryPage, error) {
tracking, err := srv.Db.GetTracking()
if err != nil {
@@ -81,7 +105,7 @@ func (srv *Service) GetRootPage(date Date) (*RootPage, error) {
// If not today detach
entry = NewEntry(date)
}
-
+
entryPage, err := srv.GetEntryPage(entry, NewDateInfo(date.ToStd()))
if err != nil {
return nil, err
@@ -91,9 +115,21 @@ func (srv *Service) GetRootPage(date Date) (*RootPage, error) {
return nil, err
}
+ tasks, err := srv.Db.GetTasks()
+ if err != nil {
+ return nil, err
+ }
+
+ task, err := srv.GetTaskPage(nil, NewTask())
+ if err != nil {
+ return nil, err
+ }
+
return &RootPage{
DateInfo: entryPage.DateInfo,
Entry: entryPage,
Entries: entries,
+ Task: task,
+ Tasks: tasks,
}, nil
}
diff --git a/templates/index.html b/templates/index.html
index b2dc6b0..18ca5f0 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -14,6 +14,10 @@
display: flex;
}
+.dir-column {
+ flex-direction: column;
+}
+
.flex-grow {
flex-grow: 1;
}
@@ -22,26 +26,39 @@
justify-content: flex-start;
}
-.week-bar {
+.just-space-between {
+ justify-content: space-between;
+}
+
+#task-bar {
margin-left: 10px;
border: 1px solid black;
}
+.short {
+ max-width: 50px;
+}
+
.status-started {
- border-color: green;
+ background-color: lightgreen;
}
.status-stopped {
- border-color: red;
+ background-color: lightblue;
}
+
.status-detached {
- border-color: blue;
+ background-color: lightyellow;
}
#entry-bar {
- border-width: 3px;
- border-style: solid;
+ border: 1px solid black;
+ padding-top: 10px;
+}
+
+#entry-form {
padding: 10px;
+ margin-buttom: 10px;
}
.entry-box {
@@ -50,6 +67,12 @@
#controls-bar {
margin-bottom: 10px;
}
+
+#taskForm {
+ background-color: lightblue;
+ padding: 10px;
+ max-width: 500px;
+}
</style>
</head>
@@ -60,7 +83,7 @@
<button hx-get="/" hx-replace-url="true" hx-params="date" hx-vals='{"date": "{{.DateInfo.Tomorrow}}"}' hx-target="body">I morgen</button>
</div>
<div class="flex">
- <div>
+ <div id="entry-bar">
{{template "entry.html" .Entry}}
<div>
<div>
@@ -80,8 +103,8 @@
</div>
</div>
</div>
- <div class="flex-grow week-bar">
- <form>
+ <div id="task-bar">
+ <form autocomplete="off">
<label for="timeRangeSelect">Vis fra: </label>
<select id="timeRangeSelect">
<option value="week">Uge</option>
@@ -96,25 +119,8 @@
<option value="m30">30 min</option>
</select>
</form>
- <table>
- <tbody>
- <tr>
- <td>Trifork</td>
- <td />
- <td />
- </tr>
- <tr>
- <td />
- <td>SVT-112: fdsa Dsa ds wqw fdsf fds</td>
- <td />
- </tr>
- <tr>
- <td />
- <td>Helligdag</td>
- <td />
- </tr>
- </tbody>
- </table>
+ {{ template "task.html" .Task }}
+ {{ template "taskRows.html" . }}
</div>
</div>
</body>
diff --git a/templates/parts/entry.html b/templates/parts/entry.html
index 300f543..52f2eb3 100644
--- a/templates/parts/entry.html
+++ b/templates/parts/entry.html
@@ -1,4 +1,4 @@
-<form id="entry-bar" autocomplete="off" class="status-{{if .Detached}}detached{{else}}{{if .Entry}}started{{else}}stopped{{end}}{{end}}">
+<form id="entry-form" 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 .Entry.Id -1 }}Redigerer opgave {{ .Entry.Id }}{{else}}Redigerer ny opgave{{end}}{{ if .Tracking }}, med opgave i baggrunden!{{else}}.{{end}}</span><br>
@@ -43,7 +43,7 @@
<button
hx-put="/save{{if .Detached}}?detached=true{{end}}"
hx-trigger="click"
- hx-target="#entry-bar"
+ hx-target="#entry-form"
hx-swap="outerHTML"
>Gem
</button>
@@ -53,7 +53,7 @@
<button
hx-get="/tracking"
hx-trigger="click"
- hx-target="#entry-bar"
+ hx-target="#entry-form"
hx-swap="outerHTML"
>Tilbage</button>
{{ end }}
@@ -62,21 +62,21 @@
<button
hx-post="/stop"
hx-trigger="click"
- hx-target="#entry-bar"
+ hx-target="#entry-form"
hx-swap="outerHTML"
>Stop</button>
{{ else }}
<button
hx-post="/start"
hx-trigger="click"
- hx-target="#entry-bar"
+ hx-target="#entry-form"
hx-swap="outerHTML"
>Start ny</button>
{{ end }}
<button
hx-get="/newDetached"
hx-trigger="click"
- hx-target="#entry-bar"
+ hx-target="#entry-form"
hx-swap="outerHTML"
>Manuel</button>
{{ end }}<input readonly type="text" name="date" {{if .Entry}}value="{{ .Entry.Date }}"{{end}} />
diff --git a/templates/parts/entryRows.html b/templates/parts/entryRows.html
index 9e9c0eb..8f10ee3 100644
--- a/templates/parts/entryRows.html
+++ b/templates/parts/entryRows.html
@@ -7,7 +7,7 @@
<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}}
+ {{ if $entry.To }}<button hx-trigger="click" hx-swap="outerHTML" hx-target="#entry-form" hx-get="/edit?id={{$entry.Id}}">e</button>{{end}}
</td>
</tr>
{{ end }}
diff --git a/templates/parts/task.html b/templates/parts/task.html
new file mode 100644
index 0000000..a366dcf
--- /dev/null
+++ b/templates/parts/task.html
@@ -0,0 +1,18 @@
+<form id="taskForm" autocomplete="off">
+ {{if gt .Task.Id -1}}
+ <label for="taskIdInput">Opgave: </label>
+ <input name="id" class="short" type="text" readonly value="{{.Task.Id}}" />
+ <button hx-get="/task/empty" hx-trigger="click" hx-target="#taskForm" hx-swap="outerHTML">X</button><br>
+ {{end}}
+ <div class="flex dir-column">
+ <input type="text" name="text" value="{{.Task.Text}}" />
+ <div class="flex just-space-between">
+ <div>
+ <label for="taskGroupInput">Gruppe: </label>
+ <input name="group" id="taskGroupInput" {{if .Task.Group}}value="{{.Task.Group}}"{{end}} type="text" />
+ </div>
+ <input class="short" name="ident" type="text" {{if .Task.Ident}}value="{{.Task.Ident}}"{{end}} />
+ </div>
+ </div>
+ <button hx-post="/task/save" hx-trigger="click" hx-target="#taskForm" hx-swap="outerHTML">Save</button>
+</form>
diff --git a/templates/parts/taskRows.html b/templates/parts/taskRows.html
new file mode 100644
index 0000000..a2a6020
--- /dev/null
+++ b/templates/parts/taskRows.html
@@ -0,0 +1,11 @@
+<table>
+ <tbody hx-swap="outerHTML" hx-trigger="changedTasks from:body" hx-get="/task/list">
+ {{ range $task := .Tasks }}
+ <tr>
+ <td>Trifork</td>
+ <td />
+ <td />
+ </tr>
+ {{ end }}
+ </tbody>
+</table>
diff --git a/tidsreg.go b/tidsreg.go
index 43e4adb..e327f74 100644
--- a/tidsreg.go
+++ b/tidsreg.go
@@ -18,7 +18,46 @@ type Server struct {
srv *Service
}
-func parseTaskFromForm(r *http.Request, useToday bool) (*Entry, error) {
+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
+ }
+ }
+
+ var group *string
+ {
+ groupStr := r.PostFormValue("group")
+ if groupStr != "" {
+ group = &groupStr
+ }
+ }
+ var ident *string
+ {
+ identStr := r.PostFormValue("ident")
+ if identStr != "" {
+ ident = &identStr
+ }
+ }
+
+ return &Task{
+ Id: int(id),
+ Group: group,
+ Ident: ident,
+ Text: r.PostFormValue("text"),
+ }, nil
+}
+
+func parseEntryFromForm(r *http.Request, useToday bool) (*Entry, error) {
err := r.ParseForm()
if err != nil {
return nil, err
@@ -59,7 +98,7 @@ func parseTaskFromForm(r *http.Request, useToday bool) (*Entry, error) {
task := &Entry{
Id: int(id),
From: from,
- Date: date,
+ Date: date,
To: to,
Comment: r.PostFormValue("comment"),
}
@@ -67,7 +106,7 @@ func parseTaskFromForm(r *http.Request, useToday bool) (*Entry, error) {
}
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")
+ tmpl, err := template.ParseFiles("templates/index.html", "templates/parts/entry.html", "templates/parts/entryRows.html", "templates/parts/task.html", "templates/parts/taskRows.html")
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
@@ -127,7 +166,7 @@ func (s *Server) rootHandle(w http.ResponseWriter, r *http.Request) {
func (s *Server) postStart(w http.ResponseWriter, r *http.Request) {
now := time.Now()
- task, err := parseTaskFromForm(r, true)
+ task, err := parseEntryFromForm(r, true)
if err != nil {
writeError(w, err.Error(), http.StatusBadRequest)
return
@@ -150,7 +189,7 @@ func (s *Server) postStart(w http.ResponseWriter, r *http.Request) {
func (s *Server) postStop(w http.ResponseWriter, r *http.Request) {
now := time.Now()
- task, err := parseTaskFromForm(r, true)
+ task, err := parseEntryFromForm(r, true)
if err != nil {
writeError(w, err.Error(), http.StatusBadRequest)
return
@@ -183,7 +222,7 @@ func (s *Server) getTracking(w http.ResponseWriter, _ *http.Request) {
}
func (s *Server) putSave(w http.ResponseWriter, r *http.Request) {
- task, err := parseTaskFromForm(r, false)
+ task, err := parseEntryFromForm(r, false)
if err != nil {
writeError(w, err.Error(), http.StatusBadRequest)
return
@@ -280,7 +319,44 @@ func (s *Server) getTasksList(w http.ResponseWriter, _ *http.Request) {
return
}
- s.renderTemplate(w, "tasksPage.html", page)
+ s.renderTemplate(w, "taskRows.html", page)
+}
+
+func (s *Server) postTaskSave(w http.ResponseWriter, r *http.Request) {
+ task, err := parseTaskFromForm(r)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ task, err = s.srv.Db.SaveTask(task)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ log.Printf("Hej %+v\n", task)
+
+ page, err := s.srv.GetTaskPage(nil, task)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Add("HX-Trigger", "changedTasks")
+
+ s.renderTemplate(w, "task.html", page)
+}
+
+func (s *Server) getTaskEmpty(w http.ResponseWriter, _ *http.Request) {
+ task := NewTask()
+
+ page, err := s.srv.GetTaskPage(nil, task)
+ if err != nil {
+ writeError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ s.renderTemplate(w, "task.html", page)
}
func main() {
@@ -300,6 +376,8 @@ func main() {
r.HandleFunc("/entryRows", s.getEntries).Methods("GET")
r.HandleFunc("/edit", s.getEdit).Methods("GET")
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("/", s.rootHandle)
http.Handle("/", r)