Skip to content

Commit 546b42f

Browse files
committed
Add small UI
1 parent 36e4aed commit 546b42f

8 files changed

Lines changed: 356 additions & 10 deletions

File tree

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,17 @@ End Time: Sun, 15 Sep 2024 22:08:14 EEST
203203
Duration: 3 minutes
204204
------------------------------
205205
```
206+
207+
## Web UI
208+
209+
### HomePage
210+
211+
![home](./img/timetracker_home.png)
212+
213+
### Start a time entry
214+
215+
![start_time](./img/timetracker_start.png)
216+
217+
### Stop a time entry
218+
219+
![stop_time](./img/timetracker_stop.png)

img/timetracker_home.png

20.8 KB
Loading

img/timetracker_start.png

20.7 KB
Loading

img/timetracker_stop.png

23.9 KB
Loading

internal/models/models.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
11
package models
22

33
import (
4-
"time"
4+
"time"
55
)
66

77
// TimeEntry represents a time tracking entry
88
type TimeEntry struct {
9-
ID uint `gorm:"primaryKey" json:"id"`
10-
Username string `json:"username"`
11-
URL string `json:"url"`
12-
Description string `json:"description"`
13-
StartTime time.Time `json:"start_time"`
14-
EndTime *time.Time `json:"end_time,omitempty"`
15-
Duration int64 `json:"duration"` // in minutes
16-
CreatedAt time.Time `json:"created_at"`
17-
UpdatedAt time.Time `json:"updated_at"`
9+
ID uint `gorm:"primaryKey" json:"id"`
10+
Username string `json:"username"`
11+
URL string `json:"url"`
12+
Description string `json:"description"`
13+
StartTime time.Time `json:"start_time"`
14+
EndTime *time.Time `json:"end_time,omitempty"`
15+
Duration int64 `json:"duration"` // in minutes
16+
CreatedAt time.Time `json:"created_at"`
17+
UpdatedAt time.Time `json:"updated_at"`
18+
}
19+
20+
// GetCurrentTime returns the current time in RFC3339 format
21+
func GetCurrentTime() string {
22+
return time.Now().Format(time.RFC3339)
23+
}
24+
25+
// CalculateDuration calculates the duration between start and end times in minutes
26+
func CalculateDuration(start, end string) int64 {
27+
layout := time.RFC3339
28+
startTime, err1 := time.Parse(layout, start)
29+
endTime, err2 := time.Parse(layout, end)
30+
if err1 != nil || err2 != nil {
31+
return 0
32+
}
33+
return int64(endTime.Sub(startTime).Minutes())
1834
}

internal/server/handlers/tracking.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,39 @@ func (h *TrackingHandler) StopTracking(w http.ResponseWriter, r *http.Request) {
107107
w.Header().Set("Content-Type", "application/json")
108108
_ = json.NewEncoder(w).Encode(entry)
109109
}
110+
111+
// GetStatus handles the /api/status endpoint
112+
func (h *TrackingHandler) GetStatus(w http.ResponseWriter, r *http.Request) {
113+
username := r.URL.Query().Get("username")
114+
if username == "" {
115+
http.Error(w, "Username is required", http.StatusBadRequest)
116+
return
117+
}
118+
119+
var activeEntry models.TimeEntry
120+
if err := h.DB.Where("username = ? AND end_time IS NULL", username).First(&activeEntry).Error; err != nil {
121+
// No active entry
122+
status := struct {
123+
Status string `json:"status"`
124+
}{
125+
Status: "idle",
126+
}
127+
w.Header().Set("Content-Type", "application/json")
128+
_ = json.NewEncoder(w).Encode(status)
129+
return
130+
}
131+
132+
// Active entry exists
133+
status := struct {
134+
Status string `json:"status"`
135+
ID uint `json:"id"`
136+
URL string `json:"url"`
137+
}{
138+
Status: "active",
139+
ID: activeEntry.ID,
140+
URL: activeEntry.URL,
141+
}
142+
143+
w.Header().Set("Content-Type", "application/json")
144+
json.NewEncoder(w).Encode(status)
145+
}

internal/server/server.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
package server
33

44
import (
5+
"embed"
56
"log"
67
"net/http"
8+
"text/template"
79

810
"github.com/gorilla/mux"
911
"github.com/xmonader/team-timetracker/internal/server/handlers"
@@ -17,22 +19,34 @@ type Server struct {
1719
TrackingHandler *handlers.TrackingHandler
1820
EntriesHandler *handlers.EntriesHandler
1921
HealthHandler *handlers.HealthHandler
22+
Templates *template.Template
2023
}
2124

25+
//go:embed templates
26+
var viewsFS embed.FS
27+
2228
// NewServer initializes the server with routes and handlers
2329
func NewServer(db *gorm.DB) *Server {
2430
router := mux.NewRouter()
2531

2632
trackingHandler := &handlers.TrackingHandler{DB: db}
2733
entriesHandler := &handlers.EntriesHandler{DB: db}
2834
healthHandler := &handlers.HealthHandler{DB: db}
35+
templates, err := template.New("").
36+
ParseFS(viewsFS,
37+
"templates/*.html",
38+
)
39+
if err != nil {
40+
panic(err)
41+
}
2942

3043
server := &Server{
3144
Router: router,
3245
DB: db,
3346
TrackingHandler: trackingHandler,
3447
EntriesHandler: entriesHandler,
3548
HealthHandler: healthHandler,
49+
Templates: templates,
3650
}
3751

3852
server.setupRoutes()
@@ -45,13 +59,28 @@ func (s *Server) setupRoutes() {
4559
// Tracking routes
4660
s.Router.HandleFunc("/api/start", s.TrackingHandler.StartTracking).Methods("POST")
4761
s.Router.HandleFunc("/api/stop", s.TrackingHandler.StopTracking).Methods("POST")
62+
s.Router.HandleFunc("/api/status", s.TrackingHandler.GetStatus).Methods("GET")
4863

4964
// Entries routes
5065
s.Router.HandleFunc("/api/entries", s.EntriesHandler.GetEntries).Methods("GET")
5166

5267
// Health and Liveness routes
5368
s.Router.HandleFunc("/live", s.HealthHandler.LivenessHandler).Methods("GET")
5469
s.Router.HandleFunc("/health", s.HealthHandler.HealthCheckHandler).Methods("GET")
70+
71+
// Webpage route
72+
s.Router.HandleFunc("/", s.HandleHome).Methods("GET")
73+
74+
}
75+
76+
// HandleHome serves the main webpage
77+
func (s *Server) HandleHome(w http.ResponseWriter, r *http.Request) {
78+
err := s.Templates.ExecuteTemplate(w, "index.html", nil)
79+
if err != nil {
80+
http.Error(w, "Error rendering template", http.StatusInternalServerError)
81+
log.Println("Template execution error:", err)
82+
return
83+
}
5584
}
5685

5786
// Run starts the HTTP server

0 commit comments

Comments
 (0)