Browse Source

add log filters for project group, project and item.

main
Gisle Aune 3 years ago
parent
commit
f7d7d99c83
  1. 14
      api/log.go
  2. 25
      database/postgres/logs.go
  3. 4
      go.mod
  4. 22
      go.sum
  5. 2
      models/log.go
  6. 11
      svelte-ui/src/clients/stufflog.ts
  7. 7
      svelte-ui/src/components/DateRangeSelect.svelte
  8. 4
      svelte-ui/src/components/ItemSelect.svelte
  9. 2
      svelte-ui/src/components/ProjectGroupSelect.svelte
  10. 9
      svelte-ui/src/components/ProjectSelect.svelte
  11. 3
      svelte-ui/src/models/log.ts
  12. 107
      svelte-ui/src/pages/LogsPage.svelte

14
api/log.go

@ -8,6 +8,7 @@ import (
"github.com/gissleh/stufflog/internal/slerrors"
"github.com/gissleh/stufflog/models"
"github.com/gissleh/stufflog/services"
"strings"
"time"
)
@ -38,6 +39,19 @@ func Log(g *gin.RouterGroup, db database.Database) {
filter.MaxTime = &maxTime
}
if value := c.Query("groups"); value != "" {
filter.ProjectGroupIDs = strings.Split(value, ",")
}
if value := c.Query("projects"); value != "" {
filter.ProjectIDs = strings.Split(value, ",")
}
if value := c.Query("items"); value != "" {
filter.ItemIDs = strings.Split(value, ",")
}
if value := c.Query("tasks"); value != "" {
filter.TaskIDs = strings.Split(value, ",")
}
return l.ListLogs(c, filter)
}))

25
database/postgres/logs.go

@ -29,26 +29,39 @@ func (r *logRepository) Find(ctx context.Context, id string) (*models.Log, error
func (r *logRepository) List(ctx context.Context, filter models.LogFilter) ([]*models.Log, error) {
sq := squirrel.Select("log.*").From("log").PlaceholderFormat(squirrel.Dollar)
sq = sq.Where(squirrel.Eq{"user_id": filter.UserID})
sq = sq.Where(squirrel.Eq{"log.user_id": filter.UserID})
if len(filter.TaskIDs) > 0 {
sq = sq.Where(squirrel.Eq{"task_id": filter.TaskIDs})
sq = sq.Where(squirrel.Eq{"log.task_id": filter.TaskIDs})
}
if len(filter.ItemIDs) > 0 {
sq = sq.Where(squirrel.Or{
squirrel.Eq{"item_id": filter.ItemIDs},
squirrel.Eq{"secondary_item_id": filter.ItemIDs},
squirrel.Eq{"log.item_id": filter.ItemIDs},
squirrel.Eq{"log.secondary_item_id": filter.ItemIDs},
})
}
if filter.MinTime != nil {
sq = sq.Where(squirrel.GtOrEq{
"logged_time": *filter.MinTime,
"log.logged_time": *filter.MinTime,
})
}
if filter.MaxTime != nil {
sq = sq.Where(squirrel.LtOrEq{
"logged_time": *filter.MaxTime,
"log.logged_time": *filter.MaxTime,
})
}
if len(filter.ProjectIDs) > 0 || len(filter.ProjectGroupIDs) > 0 {
sq = sq.InnerJoin("task AS t ON t.task_id = log.task_id")
sq = sq.InnerJoin("project AS p ON p.project_id = t.project_id")
if len(filter.ProjectGroupIDs) > 0 {
sq = sq.InnerJoin("project_group AS pg ON pg.project_group_id = p.project_group_id")
sq = sq.Where(squirrel.Eq{"pg.project_group_id": filter.ProjectGroupIDs})
}
if len(filter.ProjectIDs) > 0 {
sq = sq.Where(squirrel.Eq{"p.project_id": filter.ProjectIDs})
}
}
sq = sq.OrderBy("logged_time")

4
go.mod

@ -12,9 +12,5 @@ require (
github.com/jmoiron/sqlx v1.2.0
github.com/lib/pq v1.9.0
github.com/mattn/go-sqlite3 v1.14.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pressly/goose v2.6.0+incompatible // indirect
github.com/ziutek/mymysql v1.5.4 // indirect
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
google.golang.org/appengine v1.6.7 // indirect
)

22
go.sum

@ -43,6 +43,7 @@ github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHj
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@ -89,6 +90,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -124,8 +126,10 @@ github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubc
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
@ -170,21 +174,20 @@ github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5Vgl
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/pressly/goose v2.6.0+incompatible h1:3f8zIQ8rfgP9tyI0Hmcs2YNAqUCL1c+diLe3iU8Qd/k=
github.com/pressly/goose v2.6.0+incompatible/go.mod h1:m+QHWCqxR3k8D9l7qfzuC/djtlfzxr34mozWDYEu1z8=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -205,6 +208,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@ -226,8 +230,6 @@ github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmv
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -246,11 +248,11 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -282,6 +284,7 @@ golang.org/x/sys v0.0.0-20201016160150-f659759dc4ca h1:mLWBs1i4Qi5cHWGEtn2jieJQ2
golang.org/x/sys v0.0.0-20201016160150-f659759dc4ca/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -292,11 +295,11 @@ golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
@ -315,10 +318,12 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -328,6 +333,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

2
models/log.go

@ -69,6 +69,8 @@ type LogResult struct {
type LogFilter struct {
UserID string
ProjectGroupIDs []string
ProjectIDs []string
TaskIDs []string
ItemIDs []string
MinTime *time.Time

11
svelte-ui/src/clients/stufflog.ts

@ -131,7 +131,7 @@ export class StufflogClient {
return data.log;
}
async listLogs({minTime, maxTime}: LogFilter): Promise<LogResult[]> {
async listLogs({minTime, maxTime, itemIds, projectIds, projectGroupIds}: LogFilter): Promise<LogResult[]> {
let queries = [];
if (minTime != null) {
queries.push(`minTime=${minTime.toISOString()}`);
@ -139,6 +139,15 @@ export class StufflogClient {
if (maxTime != null) {
queries.push(`maxTime=${maxTime.toISOString()}`);
}
if (itemIds != null) {
queries.push(`items=${itemIds.join(",")}`)
}
if (projectIds != null) {
queries.push(`projects=${projectIds.join(",")}`)
}
if (projectGroupIds != null) {
queries.push(`groups=${projectGroupIds.join(",")}`)
}
const query = queries.length > 0 ? `?${queries.join("&")}` : "";

7
svelte-ui/src/components/DateRangeSelect.svelte

@ -65,14 +65,19 @@ div.styled > div:first {
}
div.styled > div {
padding: 0.5em;
padding: 0 0.5em;
margin: 0 auto;
width: 35%;
}
div.styled label {
line-height: 1.5em;
}
div.styled select, div.styled input {
width: 100%;
margin-bottom: 0.5em;
padding: 0.35em;
background: #222;
color: #777;

4
svelte-ui/src/components/ItemSelect.svelte

@ -5,6 +5,7 @@
export let name = "";
export let disabled = false;
export let optional = false;
export let enableWholeGroup = false;
$: {
if ($groupStore.stale && !$groupStore.loading) {
@ -28,6 +29,9 @@
{/if}
{#each $groupStore.groups as group (group.id)}
<optgroup label={group.name}>
{#if enableWholeGroup}
<option value={group.items.map(it => it.id).join(",")}>All "{group.name}" items</option>
{/if}
{#each group.items as item (item.id)}
<option value={item.id} selected={item.id === value}>{item.name} ({item.groupWeight})</option>
{/each}

2
svelte-ui/src/components/ProjectGroupSelect.svelte

@ -9,7 +9,7 @@
<select name={name} bind:value={value} disabled={disabled || $projectGroupStore.loading}>
{#if optional}
<option value={""} selected={"" === value}>None</option>
<option value={""} selected={"" === value}>{$projectGroupStore.loading ? "Loading..." : "None"}</option>
{/if}
{#each $projectGroupStore.groups as group (group.id)}
{#if group.id !== "META_UNGROUPED"}

9
svelte-ui/src/components/ProjectSelect.svelte

@ -12,6 +12,7 @@ import projectGroupStore from "../stores/projectGroup";
export let groupId = "";
export let disabled = false;
export let optional = false;
export let forceGroup = false;
let optGroups: OptGroup[]
@ -23,7 +24,13 @@ import projectGroupStore from "../stores/projectGroup";
if (group != null) {
projects = group.projects;
labels = group.categoryNames;
if (forceGroup && !group.projects.find(p => p.id === value)) {
value = "";
}
}
} else if (forceGroup) {
value = "";
}
optGroups = [
@ -78,7 +85,7 @@ import projectGroupStore from "../stores/projectGroup";
<select name={name} bind:value={value} disabled={disabled || $projectStore.loading}>
{#if optional}
<option value={""} selected={"" === value}>None</option>
<option value={""} selected={"" === value}>{$projectStore.loading ? "Loading..." : "None"}</option>
{/if}
{#each optGroups as group (group.status)}
{#if group.projects.length > 0}

3
svelte-ui/src/models/log.ts

@ -15,6 +15,9 @@ export default interface Log {
export interface LogFilter {
minTime?: Date
maxTime?: Date
itemIds?: string[]
projectIds?: string[]
projectGroupIds?: string[]
}
export interface LogResult extends Log {

107
svelte-ui/src/pages/LogsPage.svelte

@ -1,28 +1,52 @@
<script lang="ts">
import LogEntry from "../components/LogEntry.svelte";
import type { LogResult } from "../models/log";
import type { LogFilter, LogResult } from "../models/log";
import logStore from "../stores/logs";
import { addDays, endOfDay, endOfWeek, formatFormTime, formatWeekdayDate, startOfDay, startOfWeek } from "../utils/time";
import { addDays, formatWeekdayDate, startOfDay } from "../utils/time";
import { allOffsets, RelativeDateRange } from "../utils/date-range";
import EmptyList from "../components/EmptyList.svelte";
import RefreshSelection from "../components/RefreshSelection.svelte";
import DateRangeSelect from "../components/DateRangeSelect.svelte";
import EveryMinute from "../components/EveryMinute.svelte";
import { allOffsets, RelativeDateRange } from "../utils/date-range";
import ProjectGroupSelect from "../components/ProjectGroupSelect.svelte";
import ProjectSelect from "../components/ProjectSelect.svelte";
import ItemSelect from "../components/ItemSelect.svelte";
import projectStore from "../stores/project";
import projectGroupStore from "../stores/projectGroup";
const lsValue = localStorage.getItem("sl2.logspage.range")
let groupedLogs: {day: Date, text: string, logs: LogResult[]}[] = [];
let range = allOffsets.find(o => o.name === lsValue) || new RelativeDateRange(-7, "day");
let now = new Date();
let error = "";
let emptyMessage = `No logs since ${formatWeekdayDate(range.calculate(now)[0])}.`;
let emptyMessage = "Loading...";
let min: Date;
let max: Date;
let projectGroupId = localStorage.getItem("sl2.logspage.project_group_id");
let projectId = localStorage.getItem("sl2.logspage.project_id");
let itemId = localStorage.getItem("sl2.logspage.item_id");
$: [min, max] = range.calculate(now);
$: localStorage.setItem("sl2.logspage.range", range.name);
$: localStorage.setItem("sl2.logspage.project_group_id", projectGroupId);
$: localStorage.setItem("sl2.logspage.project_id", projectId);
$: localStorage.setItem("sl2.logspage.item_id", itemId);
$: {
if ($projectStore.stale && !$projectStore.loading) {
projectStore.load({});
}
}
$: {
if ($projectGroupStore.stale && !$projectGroupStore.loading) {
projectGroupStore.load();
}
}
$: {
error = "";
@ -30,11 +54,16 @@
if (!Number.isNaN(min.getTime()) && !Number.isNaN(max.getTime())) {
if (min.getTime() < max.getTime()) {
if (!$logStore.loading) {
if ($logStore.stale || $logStore.filter.minTime?.getTime() != min.getTime() || $logStore.filter.maxTime?.getTime() != max.getTime()) {
logStore.load({
minTime: min,
maxTime: max,
});
const filter: LogFilter = {
minTime: min,
maxTime: max,
itemIds: itemId ? itemId.split(",") : void(0),
projectIds: projectId ? [projectId] : void(0),
projectGroupIds: (!projectId && projectGroupId) ? [projectGroupId] : void(0),
}
if ($logStore.stale || JSON.stringify(filter) !== JSON.stringify($logStore.filter)) {
logStore.load(filter);
}
}
} else {
@ -47,7 +76,9 @@
$: {
if (!$logStore.loading && !$logStore.stale) {
emptyMessage = `No logs between ${formatWeekdayDate(min)} and ${formatWeekdayDate(max)}.`;
emptyMessage = `No matching logs between ${formatWeekdayDate(min)} and ${formatWeekdayDate(max)}.`;
} else {
emptyMessage = "Loading...";
}
}
@ -101,6 +132,20 @@
<div class="page">
<DateRangeSelect label="Time Filter" noFuture styled bind:value={range} />
<div class="extra-filters">
<div class="filter-group">
<label for="projectGroup">Project Group</label>
<ProjectGroupSelect name="projectGroup" optional bind:value={projectGroupId} />
</div>
<div class="filter-group">
<label for="project">Project</label>
<ProjectSelect name="project" disabled={projectGroupId === ""} optional bind:value={projectId} groupId={projectGroupId} forceGroup />
</div>
<div class="filter-group">
<label for="item">Item</label>
<ItemSelect name="item" optional bind:value={itemId} enableWholeGroup />
</div>
</div>
<div class="error">{error}</div>
<div class="log-list" class:loading={$logStore.loading}>
{#each groupedLogs as logGroup (logGroup.day.getTime())}
@ -139,6 +184,35 @@
opacity: 0.75;
}
div.extra-filters {
display: flex;
flex-direction: row;
}
div.filter-group {
flex: 1;
padding: 0 0.5em;
margin: 0 auto;
}
div.filter-group label {
line-height: 1.5em;
}
div.filter-group :global(select) {
width: 100%;
margin-bottom: 0.5em;
background: #222;
color: #777;
border: none;
outline: none;
resize: vertical;
}
div.filter-group :global(select:focus) {
background: #191919;
color: #CCC;
border: none;
outline: none;
}
h2 {
font-size: 1.5em;
font-weight: 100;
@ -146,4 +220,15 @@
margin: 0;
margin-top: 1em;
}
@media screen and (max-width: 900px) {
div.extra-filters {
flex-direction: column;
}
div.filter-group {
width: 90%;
}
}
</style>
Loading…
Cancel
Save