diff options
-rw-r--r-- | db.go | 42 | ||||
-rw-r--r-- | model.go | 17 | ||||
-rw-r--r-- | page.go | 38 | ||||
-rw-r--r-- | templates/index.html | 62 | ||||
-rw-r--r-- | templates/parts/entry.html | 12 | ||||
-rw-r--r-- | templates/parts/entryRows.html | 2 | ||||
-rw-r--r-- | templates/parts/task.html | 18 | ||||
-rw-r--r-- | templates/parts/taskRows.html | 11 | ||||
-rw-r--r-- | tidsreg.go | 92 |
9 files changed, 235 insertions, 59 deletions
@@ -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 } @@ -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")) } @@ -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> @@ -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) |