Skip to content

Commit 4590595

Browse files
committed
fix: retain query-based error loading in the updated dashboard
2 parents 9f0618c + 65933ba commit 4590595

21 files changed

Lines changed: 186 additions & 128 deletions

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ jobs:
146146
uses: docker/build-push-action@v7
147147
with:
148148
context: .
149-
file: deploy/compose/Dockerfile.kernel
149+
file: deploy/docker/Dockerfile.kernel
150150
push: false
151151
tags: webencode/kernel:latest
152152
cache-from: type=gha
@@ -155,7 +155,7 @@ jobs:
155155
uses: docker/build-push-action@v7
156156
with:
157157
context: .
158-
file: deploy/compose/Dockerfile.worker
158+
file: deploy/docker/Dockerfile.worker
159159
push: false
160160
tags: webencode/worker:latest
161161
cache-from: type=gha

go.mod

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
module github.com/rennerdo30/webencode
22

3-
go 1.26
3+
go 1.26.0
44

55
require (
66
github.com/chromedp/cdproto v0.0.0-20260714215040-dc233986426f
77
github.com/chromedp/chromedp v0.16.0
8-
github.com/coreos/go-oidc/v3 v3.20.0
9-
github.com/go-chi/chi/v5 v5.3.1
8+
github.com/coreos/go-oidc/v3 v3.21.0
9+
github.com/go-chi/chi/v5 v5.3.2
1010
github.com/go-ldap/ldap/v3 v3.4.14
1111
github.com/golang-jwt/jwt/v5 v5.3.1
1212
github.com/golang-migrate/migrate/v4 v4.19.1
@@ -15,20 +15,20 @@ require (
1515
github.com/hashicorp/go-plugin v1.8.0
1616
github.com/jackc/pgx/v5 v5.10.0
1717
github.com/minio/minio-go/v7 v7.3.0
18-
github.com/nats-io/nats-server/v2 v2.14.5
18+
github.com/nats-io/nats-server/v2 v2.14.6
1919
github.com/nats-io/nats.go v1.53.1
2020
github.com/pelletier/go-toml/v2 v2.4.3
2121
github.com/prometheus/client_golang v1.24.1
2222
github.com/spf13/viper v1.21.0
23-
github.com/stretchr/testify v1.12.0
23+
github.com/stretchr/testify v1.12.1
2424
golang.org/x/oauth2 v0.36.0
25-
google.golang.org/api v0.293.0
26-
google.golang.org/grpc v1.83.0
25+
google.golang.org/api v0.297.0
26+
google.golang.org/grpc v1.83.2
2727
google.golang.org/protobuf v1.36.12
2828
)
2929

3030
require (
31-
cloud.google.com/go/auth v0.23.0 // indirect
31+
cloud.google.com/go/auth v0.23.2 // indirect
3232
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
3333
cloud.google.com/go/compute/metadata v0.9.0 // indirect
3434
github.com/Azure/go-ntlmssp v0.1.1 // indirect
@@ -53,7 +53,7 @@ require (
5353
github.com/google/go-tpm v0.9.8 // indirect
5454
github.com/google/s2a-go v0.1.9 // indirect
5555
github.com/googleapis/enterprise-certificate-proxy v0.3.20 // indirect
56-
github.com/googleapis/gax-go/v2 v2.23.0 // indirect
56+
github.com/googleapis/gax-go/v2 v2.24.0 // indirect
5757
github.com/hashicorp/yamux v0.1.2 // indirect
5858
github.com/jackc/pgpassfile v1.0.0 // indirect
5959
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
@@ -98,7 +98,6 @@ require (
9898
golang.org/x/sys v0.47.0 // indirect
9999
golang.org/x/text v0.41.0 // indirect
100100
golang.org/x/time v0.15.0 // indirect
101-
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea // indirect
101+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 // indirect
102102
gopkg.in/ini.v1 v1.67.3 // indirect
103-
gopkg.in/yaml.v3 v3.0.1 // indirect
104103
)

go.sum

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
cloud.google.com/go/auth v0.23.0 h1:6Gg1CMgpgubRG7DGz5Vf1pcoNo8RfiRiRAPS4crTp54=
2-
cloud.google.com/go/auth v0.23.0/go.mod h1:4DhBRcqvtljQN3dJ57qtqbib5ZGCYE5f2crfiiC2EM0=
1+
cloud.google.com/go/auth v0.23.2 h1:pxSCpfiji41hpzpPdMCftEUCezpgpqmmDdYiAjCKXxo=
2+
cloud.google.com/go/auth v0.23.2/go.mod h1:4DhBRcqvtljQN3dJ57qtqbib5ZGCYE5f2crfiiC2EM0=
33
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
44
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
55
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
@@ -30,8 +30,8 @@ github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG
3030
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
3131
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
3232
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
33-
github.com/coreos/go-oidc/v3 v3.20.0 h1:EtE0WIBHk03N+DqGkY4+UONzzZHk7amKt6IyNd7OsZE=
34-
github.com/coreos/go-oidc/v3 v3.20.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
33+
github.com/coreos/go-oidc/v3 v3.21.0 h1:wZo4Q9Pum8dYEj0eMUPrqR+kvuGkeUplbLpNCkBqoWM=
34+
github.com/coreos/go-oidc/v3 v3.21.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
3535
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3636
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3737
github.com/dhui/dktest v0.4.6 h1:+DPKyScKSEp3VLtbMDHcUq6V5Lm5zfZZVb0Sk7Ahom4=
@@ -57,8 +57,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
5757
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
5858
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
5959
github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
60-
github.com/go-chi/chi/v5 v5.3.1 h1:3j4HZLGZQ3JpMCrPJF/Jl3mYJfWLKBfNJ6quurUGCf8=
61-
github.com/go-chi/chi/v5 v5.3.1/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
60+
github.com/go-chi/chi/v5 v5.3.2 h1:5YQkICvTCSZ25hoRsyJazN0scjzKGiu4VAUc7H1o1nY=
61+
github.com/go-chi/chi/v5 v5.3.2/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
6262
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
6363
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
6464
github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 h1:KZaTBSyshWX3MP5jukJcNSuXDQTO+rNpt0J564dX/eg=
@@ -96,8 +96,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
9696
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
9797
github.com/googleapis/enterprise-certificate-proxy v0.3.20 h1:t/xL64VUoN69MuMRQuJETqYGOw4Z9mSRJK9epIEtwFk=
9898
github.com/googleapis/enterprise-certificate-proxy v0.3.20/go.mod h1:L3D/IQExI6LqEjBdXcZQ1WluSgigQmSwBboFstVPM4w=
99-
github.com/googleapis/gax-go/v2 v2.23.0 h1:Tchl7qkvE7Ip3y+ztvNufYFvkfqTe7NfLTYGIdJRLuE=
100-
github.com/googleapis/gax-go/v2 v2.23.0/go.mod h1:rBQKOVJCdb8IFEzg+FCwlt1LP/xMDGuqUXhUG+XMXEg=
99+
github.com/googleapis/gax-go/v2 v2.24.0 h1:myMaPYyF9MecEmvQqMqomIwn9t/4KCZN9qnwsS76wlg=
100+
github.com/googleapis/gax-go/v2 v2.24.0/go.mod h1:IaTHBDd7NHxSCiu0vEs8pQZu4dGZrWwuSoxCnk16OFM=
101101
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
102102
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
103103
github.com/hashicorp/go-plugin v1.8.0 h1:ie8S6RRY8RvB2usYZv+AAZ/wBvx2AU5p5QeP5j/FORs=
@@ -171,8 +171,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
171171
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
172172
github.com/nats-io/jwt/v2 v2.8.2 h1:XXRgB60MSTnqsRwejQurVDs/hcv2dkt+86GjI+I/bMc=
173173
github.com/nats-io/jwt/v2 v2.8.2/go.mod h1:Ag/56sq9OblL4JgdYufDd16Egb17Kr/8WwwuO/forVc=
174-
github.com/nats-io/nats-server/v2 v2.14.5 h1:M6yeo/Xb7khi97RSEVELof3DForDqmYza3P4tHCPFWw=
175-
github.com/nats-io/nats-server/v2 v2.14.5/go.mod h1:1D3iocrisKvWaD1B/imqarTqmaGrWMqALMLbEDo3v7Q=
174+
github.com/nats-io/nats-server/v2 v2.14.6 h1:dHjEHa49lUTxIb5jrsk5kz/AmOyOQ4T9ti8djcBumDQ=
175+
github.com/nats-io/nats-server/v2 v2.14.6/go.mod h1:u/0bNBzN/m5orQGz7q2NNzxCQ6PWdwaOSwPNDADZn/A=
176176
github.com/nats-io/nats.go v1.53.1 h1:Otsq3uLc/kLdjmkNHkXH0jBqwUquwdKFoe3fq6/3/Xo=
177177
github.com/nats-io/nats.go v1.53.1/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno=
178178
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
@@ -231,8 +231,8 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
231231
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
232232
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
233233
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
234-
github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI=
235-
github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw=
234+
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
235+
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
236236
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
237237
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
238238
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
@@ -284,23 +284,20 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
284284
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
285285
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
286286
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
287-
google.golang.org/api v0.293.0 h1:p9XIWOf63U4OgYx120ZwVU8+vl4XTPmWfgVPnmOAS9w=
288-
google.golang.org/api v0.293.0/go.mod h1:6n5tjEB1gzwniZTepZ0g5u+wM7Bof5GeULCx/zh8ZE0=
289-
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
290-
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
291-
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 h1:jQ9p21COKWjP3VwuFrNRiiOTMh3mPpN45R7SLrH/HUU=
292-
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7/go.mod h1:KqHwBx2upmfa1XSi1WuRvC+2VGCLtooKkfmyvRbUmqA=
293-
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea h1:kVhQEPTpKQahD5+JSBTfBB19wcgQTTjAIn45MBqnyHk=
294-
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
295-
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
296-
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
287+
google.golang.org/api v0.297.0 h1:WktxTsnnx0yZNnsR6j0q6hR21RnnK81FHTOPy/ux4OE=
288+
google.golang.org/api v0.297.0/go.mod h1:S4m8x0M6OkQpkOzGk1y9JG2sm4fFQrMh6dxzjCTszhE=
289+
google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d h1:C9v1o0/4quuhOAfmRXA2j+we0PqZIp8traLdeogF3Ms=
290+
google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d/go.mod h1:Wz2wFJntZFmLGo7pLDXZ3wYk5hyc0Mb+SkHhDDXT+lU=
291+
google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d h1:QwnJwPte4XXAkhPu26LTDIahnsMSUV0kK8HkxbC+Pc4=
292+
google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d/go.mod h1:WRrQ7/7N19PypuT0fxLOL5Lq0waoiRri4FbtHDEKrGE=
293+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 h1:cYNAzI2sUwhmCcoj9TxvihSrqsxt6uIkj3rDRhSDmW4=
294+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA=
295+
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
296+
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
297297
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
298298
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
299299
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
300-
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
301-
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
302300
gopkg.in/ini.v1 v1.67.3 h1:iM9Lhz5MRSGhHVGGwCuzG9KO8PoirCXj/m/qTmOJJQw=
303301
gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
304302
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
305-
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
306303
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

ui/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"date-fns": "^4.1.0",
3131
"hls.js": "^1.6.15",
3232
"lucide-react": "^1.22.0",
33-
"next": "16.3.1",
33+
"next": "16.3.4",
3434
"next-intl": "^4.6.1",
3535
"react": "19.2.8",
3636
"react-dom": "19.2.8",
@@ -54,7 +54,7 @@
5454
"@vitejs/plugin-react": "^6.0.1",
5555
"@vitest/coverage-v8": "^4.1.0",
5656
"eslint": "^9",
57-
"eslint-config-next": "16.3.1",
57+
"eslint-config-next": "16.3.4",
5858
"jsdom": "^30.0.1",
5959
"tailwindcss": "^4",
6060
"typescript": "^6",

ui/src/app/[locale]/errors/page.tsx

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

3-
import { useCallback, useEffect, useState } from "react";
3+
import { useState } from "react";
4+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
45
import { CheckCircle, RefreshCw, Copy } from "lucide-react";
56
import { format } from "date-fns";
67
import { useTranslations } from "next-intl";
@@ -23,12 +24,18 @@ const SEVERITY_FALLBACK_STYLE = "badge-neutral";
2324

2425
/** Source components the API can filter on. */
2526
const SOURCE_FILTERS = ["kernel", "frontend", "worker"] as const;
26-
const ALL_SOURCES = "all";
2727

2828
/** Shared affordance for the row actions that only appear on hover/focus. */
2929
const ROW_ACTION_CLASS =
3030
"rounded-md p-2 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 focus-ring";
3131

32+
/** Base path of the error-tracking endpoints. */
33+
const ERRORS_ENDPOINT = "/api/v1/errors";
34+
/** React Query cache key for the error list. */
35+
const ERRORS_QUERY_KEY = "errors";
36+
/** Filter value that disables source filtering. */
37+
const ALL_SOURCES = "all";
38+
3239
type ErrorEvent = {
3340
id: string;
3441
source_component: string;
@@ -40,45 +47,56 @@ type ErrorEvent = {
4047
created_at: string;
4148
};
4249

50+
async function fetchErrorEvents(source: string): Promise<ErrorEvent[]> {
51+
const url = source !== ALL_SOURCES
52+
? `${ERRORS_ENDPOINT}?source=${encodeURIComponent(source)}`
53+
: ERRORS_ENDPOINT;
54+
55+
const res = await fetch(url);
56+
if (!res.ok) {
57+
throw new Error(`Failed to fetch errors: ${res.status}`);
58+
}
59+
const data = await res.json();
60+
return data ?? [];
61+
}
62+
63+
async function resolveErrorEvent(id: string): Promise<void> {
64+
const res = await fetch(`${ERRORS_ENDPOINT}/${encodeURIComponent(id)}/resolve`, {
65+
method: "PATCH",
66+
});
67+
if (!res.ok) {
68+
throw new Error(`Failed to resolve error: ${res.status}`);
69+
}
70+
}
71+
4372
export default function ErrorsPage() {
4473
const t = useTranslations('errors');
4574
const commonT = useTranslations('common');
46-
const [errors, setErrors] = useState<ErrorEvent[]>([]);
47-
const [loading, setLoading] = useState(true);
48-
const [filterSource, setFilterSource] = useState<string>(ALL_SOURCES);
49-
50-
const fetchErrors = useCallback(async () => {
51-
setLoading(true);
52-
try {
53-
const url = filterSource !== ALL_SOURCES
54-
? `/api/v1/errors?source=${filterSource}`
55-
: `/api/v1/errors`;
56-
57-
const res = await fetch(url);
58-
if (res.ok) {
59-
const data = await res.json();
60-
setErrors(data || []);
61-
}
62-
} catch (e) {
63-
console.error("Failed to fetch errors", e);
64-
} finally {
65-
setLoading(false);
66-
}
67-
}, [filterSource]);
68-
69-
useEffect(() => {
70-
void fetchErrors();
71-
}, [fetchErrors]);
72-
73-
const handleResolve = async (id: string) => {
74-
try {
75-
const res = await fetch(`/api/v1/errors/${id}/resolve`, { method: "PATCH" });
76-
if (res.ok) {
77-
await fetchErrors();
78-
}
79-
} catch (e) {
75+
const [filterSource, setFilterSource] = useState(ALL_SOURCES);
76+
const queryClient = useQueryClient();
77+
78+
const {
79+
data: errors = [],
80+
isFetching: loading,
81+
refetch,
82+
} = useQuery({
83+
queryKey: [ERRORS_QUERY_KEY, filterSource],
84+
queryFn: () => fetchErrorEvents(filterSource),
85+
});
86+
87+
88+
const resolveMutation = useMutation({
89+
mutationFn: resolveErrorEvent,
90+
onSuccess: () => {
91+
void queryClient.invalidateQueries({ queryKey: [ERRORS_QUERY_KEY] });
92+
},
93+
onError: (e) => {
8094
console.error("Failed to resolve error", e);
81-
}
95+
},
96+
});
97+
98+
const handleResolve = (id: string) => {
99+
resolveMutation.mutate(id);
82100
};
83101

84102
const getSeverityColor = (severity: string) =>
@@ -111,11 +129,12 @@ export default function ErrorsPage() {
111129
</select>
112130
<button
113131
type="button"
114-
onClick={() => void fetchErrors()}
132+
onClick={() => void refetch()}
115133
disabled={loading}
116134
aria-label={t('refresh')}
117135
title={t('refresh')}
118136
className="rounded-md p-2 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-60 focus-ring"
137+
119138
>
120139
<RefreshCw
121140
aria-hidden="true"

ui/src/components/file-browser.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ import {
2525
HardDrive,
2626
Check,
2727
} from "lucide-react";
28+
import { formatBytes } from "@/lib/utils";
29+
30+
/** Placeholder rendered where a size or date is unknown. */
31+
const EMPTY_VALUE_LABEL = "—";
2832

2933
interface FileBrowserProps {
3034
onSelect: (path: string) => void;
@@ -82,15 +86,8 @@ export function FileBrowser({ onSelect, selectedPath, mediaOnly = true }: FileBr
8286
setSearchQuery("");
8387
};
8488

85-
const formatSize = (bytes: number): string => {
86-
if (bytes === 0) return "—";
87-
const units = ["B", "KB", "MB", "GB", "TB"];
88-
const i = Math.floor(Math.log(bytes) / Math.log(1024));
89-
return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
90-
};
91-
9289
const formatDate = (timestamp: number): string => {
93-
if (!timestamp) return "—";
90+
if (!timestamp) return EMPTY_VALUE_LABEL;
9491
return new Date(timestamp * 1000).toLocaleDateString();
9592
};
9693

@@ -250,7 +247,7 @@ export function FileBrowser({ onSelect, selectedPath, mediaOnly = true }: FileBr
250247
</div>
251248
{!entry.is_directory && (
252249
<div className="text-xs text-muted-foreground flex items-center gap-2">
253-
<span>{formatSize(entry.size)}</span>
250+
<span>{formatBytes(entry.size, EMPTY_VALUE_LABEL)}</span>
254251
<span></span>
255252
<span>{formatDate(entry.mod_time)}</span>
256253
{entry.extension && (

0 commit comments

Comments
 (0)