diff --git a/.golangci.yml b/.golangci.yml index 5b6697f..116ab2d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,8 +7,6 @@ linters: - depguard - err113 - exhaustruct - - forbidigo - - forcetypeassert - gochecknoglobals - gochecknoinits - mnd @@ -22,14 +20,17 @@ linters: - wsl - wsl_v5 settings: + cyclop: + max-complexity: 11 gosmopolitan: allow-time-local: true varnamelen: max-distance: 15 ignore-decls: - - c *gin.Context - db *bbolt.DB - w *httptest.ResponseRecorder + - w http.ResponseWriter + - r *http.Request exclusions: generated: lax paths: @@ -39,13 +40,14 @@ linters: - examples$ formatters: enable: - - gci - - gofmt - gofumpt - - goimports + - golines exclusions: generated: lax paths: - third_party$ - builtin$ - examples$ +issues: + max-same-issues: 0 + max-issues-per-linter: 0 diff --git a/config.go b/config.go index f689fda..28212b8 100644 --- a/config.go +++ b/config.go @@ -1,28 +1,37 @@ package main import ( - "log" "net/http" + "strconv" "github.com/devilcove/timetraced/models" - "github.com/gin-contrib/sessions" - "github.com/gin-gonic/gin" ) -func config(c *gin.Context) { +func configOld(w http.ResponseWriter, _ *http.Request) { page := models.GetPage() - c.HTML(http.StatusOK, "config", page) + _ = templates.ExecuteTemplate(w, "config", page) } -func setConfig(c *gin.Context) { - session := sessions.Default(c) - user := session.Get("user").(string) - config := models.Config{} - if err := c.Bind(&config); err != nil { - log.Println("failed to read config", err) +func setConfig(w http.ResponseWriter, r *http.Request) { + session := sessionData(r) + user := session.User + if err := r.ParseForm(); err != nil { + processError(w, http.StatusBadRequest, "invalid data") + return + } + refresh, err := strconv.Atoi(r.FormValue("refresh")) + if err != nil { + refresh = 5 + } + config := models.Config{ + Theme: r.FormValue("theme"), + Font: r.FormValue("font"), + Refresh: refresh, } models.SetTheme(user, config.Theme) models.SetFont(user, config.Font) models.SetRefresh(user, config.Refresh) - displayMain(c) + page := models.GetUserPage(user) + w.Header().Set("Hx-Refresh", "true") + _ = templates.ExecuteTemplate(w, "layout", page) } diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..e049962 --- /dev/null +++ b/config_test.go @@ -0,0 +1,40 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/Kairum-Labs/should" +) + +func TestConfig(t *testing.T) { + createAdmin() + c := adminLogin() + if c == nil { + t.FailNow() + } + t.Run("get", func(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/config/", nil) + r.AddCookie(adminLogin()) + router.ServeHTTP(w, r) + should.BeEqual(t, w.Result().StatusCode, http.StatusOK) + }) + t.Run("update", func(t *testing.T) { + w := httptest.NewRecorder() + body := bodyParams("theme", "red", "font", "tangerine", "refesh", "10") + r := httptest.NewRequest(http.MethodPost, "/config/", body) + r.AddCookie(adminLogin()) + router.ServeHTTP(w, r) + should.BeEqual(t, w.Result().StatusCode, http.StatusOK) + }) + t.Run("invalid refresh", func(t *testing.T) { + w := httptest.NewRecorder() + body := bodyParams("theme", "red", "font", "tangerine", "refesh", "junk") + r := httptest.NewRequest(http.MethodPost, "/config/", body) + r.AddCookie(adminLogin()) + router.ServeHTTP(w, r) + should.BeEqual(t, w.Result().StatusCode, http.StatusOK) + }) +} diff --git a/database/project_test.go b/database/project_test.go index 5cff0a5..81c2051 100644 --- a/database/project_test.go +++ b/database/project_test.go @@ -38,3 +38,42 @@ func TestGetProject(t *testing.T) { should.BeEqual(t, project, models.Project{}) }) } + +func TestGetAllProjects(t *testing.T) { + projects, err := GetAllProjects() + t.Log(projects, err) + should.BeNil(t, err) + should.NotBeEmpty(t, projects) +} + +func TestDeleteProject(t *testing.T) { + err := SaveProject(&models.Project{ + ID: uuid.New(), + Name: "toBeDeleted", + Active: true, + Updated: time.Now(), + }) + should.BeNil(t, err) + err = DeleteProject("tobeDeleted") + should.BeNil(t, err) +} + +func TestGetActiveProject(t *testing.T) { + err := SaveProject(&models.Project{ + ID: uuid.New(), + Name: "one", + Active: true, + Updated: time.Now(), + }) + should.BeNil(t, err) + should.BeNil(t, deleteAllRecords()) + should.BeNil(t, createTestRecords()) + t.Run("ok", func(t *testing.T) { + project := GetActiveProject("testUser") + should.BeEqual(t, project.Name, "one") + }) + t.Run("none", func(t *testing.T) { + project := GetActiveProject("user1") + should.BeNil(t, project) + }) +} diff --git a/database/records_test.go b/database/records_test.go index 4eaaa92..766abbd 100644 --- a/database/records_test.go +++ b/database/records_test.go @@ -3,6 +3,10 @@ package database import ( "testing" "time" + + "github.com/Kairum-Labs/should" + "github.com/devilcove/timetraced/models" + "github.com/google/uuid" ) func Test_truncateToStart(t *testing.T) { @@ -56,3 +60,131 @@ func Test_truncateToEnd(t *testing.T) { }) } } + +func TestSaveRecord(t *testing.T) { + should.BeNil(t, deleteAllRecords()) + err := SaveRecord(&models.Record{ + ID: uuid.New(), + Project: "one", + User: "testUser", + Start: time.Now().Add(time.Hour * -1), + End: time.Now(), + }) + should.BeNil(t, err) +} + +func TestGetRecord(t *testing.T) { + should.BeNil(t, deleteAllRecords()) + should.BeNil(t, createTestRecords()) + records, err := GetAllRecords() + should.BeNil(t, err) + should.BeEqual(t, len(records), 3) + record, err := GetRecord(records[0].ID) + should.BeNil(t, err) + should.BeEqual(t, record.User, records[0].User) +} + +func TestGetTodaysRecords(t *testing.T) { + should.BeNil(t, deleteAllRecords()) + should.BeNil(t, createTestRecords()) + t.Run("all", func(t *testing.T) { + records, err := GetTodaysRecords() + should.BeNil(t, err) + should.BeEqual(t, len(records), 3) + }) + t.Run("forUser", func(t *testing.T) { + records, err := GetTodaysRecordsForUser("testUser") + should.BeNil(t, err) + should.BeEqual(t, len(records), 2) + }) +} + +func TestGetReportRecords(t *testing.T) { + should.BeNil(t, deleteAllRecords()) + should.BeNil(t, createTestRecords()) + t.Run("today", func(t *testing.T) { + records, err := GetReportRecords(models.DatabaseReportRequest{ + Start: time.Now(), + End: time.Now(), + Project: "one", + User: "testUser", + }) + should.BeNil(t, err) + should.BeEqual(t, len(records), 2) + }) + t.Run("yesterday", func(t *testing.T) { + records, err := GetReportRecords(models.DatabaseReportRequest{ + Start: time.Now().Add(time.Hour * -24 * 7), + End: time.Now().Add(time.Hour * -24), + Project: "one", + User: "testUser", + }) + should.BeNil(t, err) + should.BeEqual(t, len(records), 0) + }) +} + +func TestDeleteRecords(t *testing.T) { + should.BeNil(t, deleteAllRecords()) + should.BeNil(t, createTestRecords()) + records, err := GetAllRecords() + should.BeNil(t, err) + err = DeleteRecord(records[0].ID) + should.BeNil(t, err) + remainder, err := GetAllRecords() + should.BeNil(t, err) + should.BeLessThan(t, len(remainder), len(records)) +} + +func TestGetAllRecordsForUser(t *testing.T) { + should.BeNil(t, deleteAllRecords()) + should.BeNil(t, createTestRecords()) + records, err := GetAllRecordsForUser("testUser") + should.BeNil(t, err) + should.BeEqual(t, len(records), 2) +} + +func createTestRecords() error { + records := []models.Record{ + { + ID: uuid.New(), + Project: "one", + User: "testUser", + Start: time.Now().Add(time.Hour * -1), + // End: time.Now(), + }, + { + ID: uuid.New(), + Project: "one", + User: "testUser", + Start: time.Now().Add(time.Hour * -2), + End: time.Now().Add(time.Hour * -1), + }, + { + ID: uuid.New(), + Project: "two", + User: "user1", + Start: time.Now().Add(time.Hour * -2), + End: time.Now().Add(time.Hour * -1), + }, + } + for _, record := range records { + if err := SaveRecord(&record); err != nil { + return err + } + } + return nil +} + +func deleteAllRecords() error { + records, err := GetAllRecords() + if err != nil { + return err + } + for _, record := range records { + if err := DeleteRecord(record.ID); err != nil { + return err + } + } + return nil +} diff --git a/database/user_test.go b/database/user_test.go new file mode 100644 index 0000000..bf81cca --- /dev/null +++ b/database/user_test.go @@ -0,0 +1,106 @@ +package database + +import ( + "testing" + + "github.com/Kairum-Labs/should" + "github.com/devilcove/timetraced/models" + "golang.org/x/crypto/bcrypt" +) + +func TestSaveUser(t *testing.T) { + err := SaveUser(&models.User{ + Username: "testUser", + Password: "don't care", + }) + should.BeNil(t, err) + should.BeNil(t, deleteAllUsers()) +} + +func TestDeleteUser(t *testing.T) { + err := createTestUser(models.User{ + Username: "testUser", + Password: "don't care", + }) + should.BeNil(t, err) + err = DeleteUser("testUser") + should.BeNil(t, err) + err = DeleteUser("testUser") // deleting a non-exitent entry does not return error + should.BeNil(t, err) +} + +func TestGetUsers(t *testing.T) { + should.BeNil(t, deleteAllUsers()) + t.Run("no users", func(t *testing.T) { + user, err := GetUser("testUser") + should.NotBeNil(t, err) + should.BeEqual(t, user, models.User{}) + }) + t.Run("one user", func(t *testing.T) { + should.BeNil(t, createTestUser(models.User{ + Username: "testUser", + })) + user, err := GetUser("testUser") + should.BeNil(t, err) + should.BeEqual(t, user.Username, "testUser") + }) + t.Run("multiple users", func(t *testing.T) { + should.BeNil(t, createTestUser(models.User{ + Username: "user2", + })) + user, err := GetUser("testUser") + should.BeNil(t, err) + should.BeEqual(t, user.Username, "testUser") + }) +} + +func TestGetAllUsers(t *testing.T) { + should.BeNil(t, deleteAllUsers()) + t.Run("no users", func(t *testing.T) { + users, err := GetAllUsers() + should.BeNil(t, err) + should.BeEmpty(t, users) + }) + t.Run("one user", func(t *testing.T) { + should.BeNil(t, createTestUser(models.User{ + Username: "testUser", + })) + users, err := GetAllUsers() + should.BeNil(t, err) + should.BeEqual(t, len(users), 1) + }) + t.Run("multiple users", func(t *testing.T) { + should.BeNil(t, createTestUser(models.User{ + Username: "user2", + })) + users, err := GetAllUsers() + should.BeNil(t, err) + should.BeGreaterThan(t, len(users), 1) + }) +} + +func createTestUser(user models.User) error { + user.Password, _ = hashPassword(user.Password) + if err := SaveUser(&user); err != nil { + return err + } + return nil +} + +func hashPassword(password string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), 4) + return string(bytes), err +} + +func deleteAllUsers() error { + users, err := GetAllUsers() + if err != nil { + return nil + } + for _, user := range users { + if err := DeleteUser(user.Username); err != nil { + return err + } + } + return nil +} diff --git a/go.mod b/go.mod index 4bca46d..dceec9a 100644 --- a/go.mod +++ b/go.mod @@ -3,47 +3,18 @@ module github.com/devilcove/timetraced go 1.24.0 require ( - github.com/Kairum-Labs/should v0.2.1 - github.com/gin-contrib/sessions v1.0.4 - github.com/gin-gonic/gin v1.11.0 + github.com/Kairum-Labs/should v0.2.2 + github.com/devilcove/mux v0.2.0 github.com/google/uuid v1.6.0 - github.com/joho/godotenv v1.5.1 + github.com/gorilla/sessions v1.4.0 + github.com/mattkasun/tools v0.2.3 go.etcd.io/bbolt v1.4.3 - golang.org/x/crypto v0.45.0 + golang.org/x/crypto v0.42.0 ) require ( - github.com/bytedance/sonic v1.14.0 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.27.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect - github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect - github.com/gorilla/sessions v1.4.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.54.1 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.0 // indirect - go.uber.org/mock v0.5.0 // indirect - golang.org/x/arch v0.20.0 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect - golang.org/x/tools v0.38.0 // indirect - google.golang.org/protobuf v1.36.9 // indirect + github.com/stretchr/testify v1.11.1 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.39.0 // indirect ) diff --git a/go.sum b/go.sum index 62f7665..cee9b59 100644 --- a/go.sum +++ b/go.sum @@ -1,107 +1,30 @@ -github.com/Kairum-Labs/should v0.2.1 h1:20TZTcmvu9Mwskiqn1jmHo63343vejkhc7RGuDAwPs4= -github.com/Kairum-Labs/should v0.2.1/go.mod h1:vP/ASEjUAKoWy/M7uIrAXq69p7/IUWOpEe5R+q/+K34= -github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= -github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/Kairum-Labs/should v0.2.2 h1:bO1kaMGRYRSSpq8MHsmk2MqpA1taLPblPCZy9Jnpt+U= +github.com/Kairum-Labs/should v0.2.2/go.mod h1:vP/ASEjUAKoWy/M7uIrAXq69p7/IUWOpEe5R+q/+K34= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= -github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U= -github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= -github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/devilcove/mux v0.2.0 h1:U9bKQs0HhGJqIFUmH84nPt5pz67QJtmVtwT2f3RKgsk= +github.com/devilcove/mux v0.2.0/go.mod h1:Q4ysJcjpLwW7rLNTOYSlYVst5OmwFh1uZxJSCPQ34U4= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= -github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/mattkasun/tools v0.2.3 h1:g3ciN2dtA+NpTGqJvcuMaHy4NRkPKWt8TY8ZnJw8kVc= +github.com/mattkasun/tools v0.2.3/go.mod h1:WHhDjcX5n0ZAP0Z6XET1p2pCWqUgZDq8M5XEg+Pus9k= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.54.1 h1:4ZAWm0AhCb6+hE+l5Q1NAL0iRn/ZrMwqHRGQiFwj2eg= -github.com/quic-go/quic-go v0.54.1/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= -github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= -golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/html/about.html b/html/about.html index ace0dab..72a9eb5 100644 --- a/html/about.html +++ b/html/about.html @@ -6,8 +6,8 @@

TimeTrace

Version {{ .Version }}

© 2023-2024 Matthew R. Kasun

- built with gin bbolt - and htmx

+ built with bbolt + and htmx

mkasun@nusak.ca
@@ -15,7 +15,8 @@

© 2023-2024 Matthew R. Kasun

repo

- + diff --git a/html/configuration.html b/html/configuration.html index 3e52c81..6128c1c 100644 --- a/html/configuration.html +++ b/html/configuration.html @@ -3,7 +3,7 @@

Configuration

-
+


-
-
- -

-
- Don't have an Account? Register -
-
+
+

Timetrace Login

+ +
+
+
+
+
+

+ +
+
+ {{end}} \ No newline at end of file diff --git a/html/navbar.html b/html/navbar.html index ca72e7e..95ff251 100644 --- a/html/navbar.html +++ b/html/navbar.html @@ -11,26 +11,23 @@ Projects
{{range .Projects }} -
{{end}}
-
-
+ +
- Logout + Logout - - - - - -

+
+

+ +

diff --git a/html/register.html b/html/register.html index 1541e7a..5a39843 100644 --- a/html/register.html +++ b/html/register.html @@ -1,17 +1,20 @@ {{define "register"}} -
-
-

Timetrace Registration

-
-
-
-
-
- -
- +
+
+

Timetrace Add User

+
+
+
+
+
+
+
+

+ +
+
+ Cancel
+
{{end}} \ No newline at end of file diff --git a/html/report.html b/html/report.html index 6cb484d..d65cbcd 100644 --- a/html/report.html +++ b/html/report.html @@ -3,7 +3,7 @@

Reports

-
+

@@ -38,7 +38,8 @@

Project {{.Project}}

{{end}}

{{.Total}}

{{end}} - +
{{end}} diff --git a/html/user.html b/html/user.html index f9756da..c3a77e8 100644 --- a/html/user.html +++ b/html/user.html @@ -16,10 +16,14 @@

Users

{{.Username}} {{.IsAdmin}} - + {{end}} +
+

New User  

+
{{end}} @@ -29,18 +33,24 @@

Users


Edit User

-

{{.Username}}

+

{{.Username}} {{.IsAdmin}}

-
+


+ {{ if .AsAdmin }} + +

+ + {{else}} + {{end }}